@@ -20,7 +20,6 @@ public interface IAzureDevOpsRestApi
20
20
21
21
public class AzureDevOpsRestApi : IAzureDevOpsRestApi
22
22
{
23
-
24
23
private readonly ICommandContext _context ;
25
24
private readonly IHttpClientFactory _httpFactory ;
26
25
@@ -44,12 +43,8 @@ public async Task<string> GetAuthorityAsync(Uri organizationUri)
44
43
const string commonAuthority = authorityBase + "common" ;
45
44
const string msaAuthority = authorityBase + "live.com" ;
46
45
47
- HttpClient client = _httpFactory . GetClient ( ) ;
48
- client . DefaultRequestHeaders . Accept . Add ( new MediaTypeWithQualityHeaderValue ( Constants . Http . MimeTypeJson ) ) ;
49
-
50
46
_context . Trace . WriteLine ( $ "HTTP: HEAD { organizationUri } ") ;
51
- using ( client )
52
- using ( var response = await client . HeadAsync ( organizationUri ) )
47
+ using ( HttpResponseMessage response = await HttpClient . HeadAsync ( organizationUri ) )
53
48
{
54
49
_context . Trace . WriteLine ( "HTTP: Response code ignored." ) ;
55
50
_context . Trace . WriteLine ( "Inspecting headers..." ) ;
@@ -113,14 +108,10 @@ public async Task<string> CreatePersonalAccessTokenAsync(Uri organizationUri, st
113
108
114
109
Uri requestUri = new Uri ( identityServiceUri , sessionTokenUrl ) ;
115
110
116
- HttpClient client = _httpFactory . GetClient ( ) ;
117
- client . DefaultRequestHeaders . Accept . Add ( new MediaTypeWithQualityHeaderValue ( Constants . Http . MimeTypeJson ) ) ;
118
- client . DefaultRequestHeaders . Authorization = new AuthenticationHeaderValue ( Constants . Http . WwwAuthenticateBearerScheme , accessToken ) ;
119
-
120
111
_context . Trace . WriteLine ( $ "HTTP: POST { requestUri } ") ;
121
- using ( client )
122
112
using ( StringContent content = CreateAccessTokenRequestJson ( organizationUri , scopes ) )
123
- using ( var response = await client . PostAsync ( requestUri , content ) )
113
+ using ( HttpRequestMessage request = CreateRequestMessage ( HttpMethod . Post , requestUri , content , accessToken ) )
114
+ using ( HttpResponseMessage response = await HttpClient . SendAsync ( request ) )
124
115
{
125
116
_context . Trace . WriteLine ( $ "HTTP: Response { ( int ) response . StatusCode } [{ response . StatusCode } ]") ;
126
117
@@ -161,13 +152,9 @@ private async Task<Uri> GetIdentityServiceUriAsync(Uri organizationUri, string a
161
152
Query = locationServiceQuery ,
162
153
} . Uri ;
163
154
164
- HttpClient client = _httpFactory . GetClient ( ) ;
165
- client . DefaultRequestHeaders . Accept . Add ( new MediaTypeWithQualityHeaderValue ( Constants . Http . MimeTypeJson ) ) ;
166
- client . DefaultRequestHeaders . Authorization = new AuthenticationHeaderValue ( Constants . Http . WwwAuthenticateBearerScheme , accessToken ) ;
167
-
168
155
_context . Trace . WriteLine ( $ "HTTP: GET { requestUri } ") ;
169
- using ( client )
170
- using ( var response = await client . GetAsync ( requestUri ) )
156
+ using ( HttpRequestMessage request = CreateRequestMessage ( HttpMethod . Get , requestUri , bearerToken : accessToken ) )
157
+ using ( HttpResponseMessage response = await HttpClient . SendAsync ( request ) )
171
158
{
172
159
_context . Trace . WriteLine ( $ "HTTP: Response { ( int ) response . StatusCode } [{ response . StatusCode } ]") ;
173
160
if ( response . IsSuccessStatusCode )
@@ -191,6 +178,24 @@ private async Task<Uri> GetIdentityServiceUriAsync(Uri organizationUri, string a
191
178
192
179
private const RegexOptions CommonRegexOptions = RegexOptions . Compiled | RegexOptions . CultureInvariant | RegexOptions . IgnoreCase ;
193
180
181
+ private HttpClient _httpClient ;
182
+
183
+ private HttpClient HttpClient
184
+ {
185
+ get
186
+ {
187
+ if ( _httpClient is null )
188
+ {
189
+ _httpClient = _httpFactory . CreateClient ( ) ;
190
+
191
+ // Configure the HTTP client with standard headers for Azure Repos API calls
192
+ _httpClient . DefaultRequestHeaders . Accept . Add ( new MediaTypeWithQualityHeaderValue ( Constants . Http . MimeTypeJson ) ) ;
193
+ }
194
+
195
+ return _httpClient ;
196
+ }
197
+ }
198
+
194
199
/// <summary>
195
200
/// Attempt to extract the authority from a Authorization Bearer header.
196
201
/// </summary>
@@ -267,6 +272,31 @@ private static StringContent CreateAccessTokenRequestJson(Uri organizationUri, I
267
272
return content ;
268
273
}
269
274
275
+ /// <summary>
276
+ /// Create an <see cref="HttpRequestMessage"/> with optional content and bearer-token authorization header.
277
+ /// </summary>
278
+ /// <param name="method">HTTP request method type.</param>
279
+ /// <param name="uri">Request URI.</param>
280
+ /// <param name="content">Optional request content.</param>
281
+ /// <param name="bearerToken">Optional bearer token for authorization.</param>
282
+ /// <returns>HTTP request message.</returns>
283
+ private static HttpRequestMessage CreateRequestMessage ( HttpMethod method , Uri uri , HttpContent content = null , string bearerToken = null )
284
+ {
285
+ var request = new HttpRequestMessage ( method , uri ) ;
286
+
287
+ if ( ! ( content is null ) )
288
+ {
289
+ request . Content = content ;
290
+ }
291
+
292
+ if ( ! ( bearerToken is null ) )
293
+ {
294
+ request . Headers . Authorization = new AuthenticationHeaderValue ( Constants . Http . WwwAuthenticateBearerScheme , bearerToken ) ;
295
+ }
296
+
297
+ return request ;
298
+ }
299
+
270
300
#endregion
271
301
}
272
302
}
0 commit comments