1+ using System ;
2+ using System . Collections . Generic ;
3+ using System . Linq ;
4+ using System . Net . Http ;
5+ using System . Text . RegularExpressions ;
6+ using Microsoft . VisualStudio . Services . Common ;
7+ using Microsoft . VisualStudio . Services . WebApi ;
8+
9+ namespace TfsCmdlets
10+ {
11+ public class GenericHttpClient : VssHttpClientBase
12+ {
13+ #region Constructors and fields
14+
15+ public GenericHttpClient ( Uri baseUrl , VssCredentials credentials ) : base ( baseUrl , credentials )
16+ {
17+ }
18+
19+ public GenericHttpClient ( Uri baseUrl , VssCredentials credentials , VssHttpRequestSettings settings ) : base (
20+ baseUrl , credentials , settings )
21+ {
22+ }
23+
24+ public GenericHttpClient ( Uri baseUrl , VssCredentials credentials , params DelegatingHandler [ ] handlers ) : base (
25+ baseUrl , credentials , handlers )
26+ {
27+ }
28+
29+ public GenericHttpClient ( Uri baseUrl , HttpMessageHandler pipeline , bool disposeHandler ) : base ( baseUrl ,
30+ pipeline , disposeHandler )
31+ {
32+ }
33+
34+ public GenericHttpClient ( Uri baseUrl , VssCredentials credentials , VssHttpRequestSettings settings ,
35+ params DelegatingHandler [ ] handlers ) : base ( baseUrl , credentials , settings , handlers )
36+ {
37+ }
38+
39+ #endregion
40+
41+ /// <summary>
42+ /// Sends a GET request to an Azure DevOps API
43+ /// </summary>
44+ /// <typeparam name="T">The typed model (JSON proxy class) corresponding to the API result</typeparam>
45+ /// <param name="apiPath">The path to the API, relative to the collection/organization URL</param>
46+ /// <param name="apiVersion">The version of the requested API (e.g. "5.1")</param>
47+ /// <param name="additionalHeaders">Any additional HTTP headers that must be sent along the request</param>
48+ /// <param name="queryParameters">Any query parameters ("query string") that are part of the request</param>
49+ /// <param name="mediaType">The MIME content type of the response</param>
50+ /// <param name="userState">User-defined, arbitrary data sent as a "userstate" HTTP header</param>
51+ /// <returns>The response of the API, converted to the model type T</returns>
52+ public T Get < T > (
53+ string apiPath ,
54+ string apiVersion = "1.0" ,
55+ IDictionary < string , string > additionalHeaders = null ,
56+ IDictionary < string , string > queryParameters = null ,
57+ string mediaType = "application/json" ,
58+ object userState = null )
59+ {
60+ var msg = CreateMessage ( HttpMethod . Get , apiPath , apiVersion ,
61+ additionalHeaders , queryParameters , mediaType ,
62+ null , null ) ;
63+
64+ return SendAsync < T > ( msg , userState ) . GetAwaiter ( ) . GetResult ( ) ;
65+ }
66+
67+ /// <summary>
68+ /// Sends a GET request to an Azure DevOps API
69+ /// </summary>
70+ /// <param name="apiPath">The path to the API, relative to the collection/organization URL</param>
71+ /// <param name="apiVersion">The version of the requested API (e.g. "5.1")</param>
72+ /// <param name="additionalHeaders">Any additional HTTP headers that must be sent along the request</param>
73+ /// <param name="queryParameters">Any query parameters ("query string") that are part of the request</param>
74+ /// <param name="mediaType">The MIME content type of the response</param>
75+ /// <param name="userState">User-defined, arbitrary data sent as a "userstate" HTTP header</param>
76+ /// <returns>The response of the API as an HttpResponseMessage object</returns>
77+ public HttpResponseMessage Get (
78+ string apiPath ,
79+ string apiVersion = "1.0" ,
80+ IDictionary < string , string > additionalHeaders = null ,
81+ IDictionary < string , string > queryParameters = null ,
82+ string mediaType = "application/json" ,
83+ object userState = null )
84+ {
85+ var msg = CreateMessage ( HttpMethod . Get , apiPath , apiVersion ,
86+ additionalHeaders , queryParameters ,
87+ mediaType , null , null ) ;
88+
89+ return Send ( msg , userState ) ;
90+ }
91+
92+ /// <summary>
93+ /// Sends a POST request to an Azure DevOps API
94+ /// </summary>
95+ /// <typeparam name="T">The typed model (JSON proxy class) corresponding to the API parameter (content) object</typeparam>
96+ /// <typeparam name="TResult">The typed model (JSON proxy class) corresponding to the API result</typeparam>
97+ /// <param name="apiPath">The path to the API, relative to the collection/organization URL</param>
98+ /// <param name="value">The API parameters sent as the request body</param>
99+ /// <param name="apiVersion">The version of the requested API (e.g. "5.1")</param>
100+ /// <param name="additionalHeaders">Any additional HTTP headers that must be sent along the request</param>
101+ /// <param name="queryParameters">Any query parameters ("query string") that are part of the request</param>
102+ /// <param name="mediaType">The MIME content type of the response</param>
103+ /// <param name="userState">User-defined, arbitrary data sent as a "userstate" HTTP header</param>
104+ /// <returns>The response of the API as an HttpResponseMessage object</returns>
105+ public TResult Post < T , TResult > (
106+ string apiPath ,
107+ T value ,
108+ string apiVersion = "1.0" ,
109+ IDictionary < string , string > additionalHeaders = null ,
110+ IDictionary < string , string > queryParameters = null ,
111+ string mediaType = "application/json" ,
112+ object userState = null )
113+ {
114+ var content = new ObjectContent < T > ( value , new VssJsonMediaTypeFormatter ( ) ) ;
115+
116+ var msg = CreateMessage ( HttpMethod . Post , apiPath , apiVersion ,
117+ additionalHeaders , queryParameters ,
118+ mediaType , null , content ) ;
119+
120+ var result = SendAsync < TResult > ( msg , userState ) . GetAwaiter ( ) . GetResult ( ) ;
121+
122+ return result ;
123+ }
124+
125+ /// <summary>
126+ /// Sends a POST request to an Azure DevOps API
127+ /// </summary>
128+ /// <param name="apiPath">The path to the API, relative to the collection/organization URL</param>
129+ /// <param name="content">The API parameters sent as the request body</param>
130+ /// <param name="apiVersion">The version of the requested API (e.g. "5.1")</param>
131+ /// <param name="additionalHeaders">Any additional HTTP headers that must be sent along the request</param>
132+ /// <param name="queryParameters">Any query parameters ("query string") that are part of the request</param>
133+ /// <param name="mediaType">The MIME content type of the response</param>
134+ /// <param name="userState">User-defined, arbitrary data sent as a "userstate" HTTP header</param>
135+ /// <returns>The response of the API as an HttpResponseMessage object</returns>
136+ public HttpResponseMessage Post (
137+ string apiPath ,
138+ HttpContent content ,
139+ string apiVersion = "1.0" ,
140+ IDictionary < string , string > additionalHeaders = null ,
141+ IDictionary < string , string > queryParameters = null ,
142+ string mediaType = "application/json" ,
143+ object userState = null )
144+ {
145+ var msg = CreateMessage ( HttpMethod . Post , apiPath , apiVersion ,
146+ additionalHeaders , queryParameters ,
147+ mediaType , null , content ) ;
148+
149+ return Send ( msg , userState ) ;
150+ }
151+
152+ public T PostForm < T > (
153+ string formPath ,
154+ Dictionary < string , string > formData ,
155+ bool sendVerificationToken = false ,
156+ string tokenRequestPath = null ,
157+ IDictionary < string , string > additionalHeaders = null ,
158+ IDictionary < string , string > queryParameters = null ,
159+ string responseMediaType = "text/html" ,
160+ object userState = null )
161+ {
162+ if ( formData == null ) { throw new ArgumentNullException ( nameof ( formData ) ) ; }
163+
164+ if ( sendVerificationToken )
165+ {
166+ var response = Get ( tokenRequestPath ) ;
167+ var html = response . Content . ReadAsStringAsync ( ) . GetAwaiter ( ) . GetResult ( ) ;
168+ var matches = Regex . Match ( html , "name=\" __RequestVerificationToken\" value=\" ([^\\ \" ]*)" ) ;
169+
170+ if ( matches . Groups . Count > 0 )
171+ {
172+ formData [ "__RequestVerificationToken" ] = matches . Groups [ 1 ] . Value ;
173+ }
174+
175+ var cookies = response . Headers . GetValues ( "Set-Cookie" ) ? . ToList ( ) ;
176+
177+ if ( cookies ? . Count > 0 )
178+ {
179+ if ( additionalHeaders == null )
180+ {
181+ additionalHeaders = new Dictionary < string , string > ( ) ;
182+ }
183+
184+ var tokenCookie = string . Join ( "; " , cookies
185+ . Where ( c => c . StartsWith ( "__RequestVerificationToken" ) )
186+ . Select ( c => c . Substring ( 0 , c . IndexOf ( ';' ) ) ) ) ;
187+
188+ if ( additionalHeaders . ContainsKey ( "Cookie" ) )
189+ {
190+ var presetCookie = additionalHeaders [ "Cookie" ] ;
191+ additionalHeaders [ "Cookie" ] = $ "{ tokenCookie } ;{ presetCookie } ";
192+ }
193+ else
194+ {
195+ additionalHeaders [ "Cookie" ] = $ "{ tokenCookie } ;";
196+ }
197+ }
198+ }
199+
200+ var content = new FormUrlEncodedContent ( formData ) ;
201+
202+ var msg = CreateMessage ( HttpMethod . Post , formPath , "1" ,
203+ additionalHeaders , queryParameters ,
204+ responseMediaType , null , content ) ;
205+
206+ return SendAsync < T > ( msg , userState ) . GetAwaiter ( ) . GetResult ( ) ;
207+ }
208+
209+ private HttpRequestMessage CreateMessage ( HttpMethod method , string apiPath , string apiVersion ,
210+ IDictionary < string , string > additionalHeaders ,
211+ IDictionary < string , string > queryParameters , string mediaType , object routeValues ,
212+ HttpContent content )
213+ {
214+ var msg = CreateRequestMessage (
215+ method ,
216+ additionalHeaders ,
217+ new ApiResourceLocation
218+ {
219+ ReleasedVersion = new Version ( 1 , 0 ) ,
220+ MinVersion = new Version ( 1 , 0 ) ,
221+ MaxVersion = new Version ( 9 , 9 ) ,
222+ RouteTemplate = apiPath ,
223+ ResourceVersion = 1
224+ } ,
225+ routeValues ,
226+ new ApiResourceVersion ( apiVersion ) ,
227+ content ,
228+ queryParameters ,
229+ mediaType ) ;
230+ return msg ;
231+ }
232+ }
233+ }
0 commit comments