LEFT | RIGHT |
1 /* | 1 /* |
2 Copyright 2013 Google Inc | 2 Copyright 2013 Google Inc |
3 | 3 |
4 Licensed under the Apache License, Version 2.0 (the "License"); | 4 Licensed under the Apache License, Version 2.0 (the "License"); |
5 you may not use this file except in compliance with the License. | 5 you may not use this file except in compliance with the License. |
6 You may obtain a copy of the License at | 6 You may obtain a copy of the License at |
7 | 7 |
8 http://www.apache.org/licenses/LICENSE-2.0 | 8 http://www.apache.org/licenses/LICENSE-2.0 |
9 | 9 |
10 Unless required by applicable law or agreed to in writing, software | 10 Unless required by applicable law or agreed to in writing, software |
(...skipping 25 matching lines...) Expand all Loading... |
36 | 36 |
37 namespace Google.Apis.Services | 37 namespace Google.Apis.Services |
38 { | 38 { |
39 /// <summary> | 39 /// <summary> |
40 /// A thread-safe base class for a client service which provides common mech
anism for all services, like· | 40 /// A thread-safe base class for a client service which provides common mech
anism for all services, like· |
41 /// serialization and GZip support. | 41 /// serialization and GZip support. |
42 /// This class adds a special <see cref="Google.Apis.Http.IHttpExecuteInterc
eptor"/> to the· | 42 /// This class adds a special <see cref="Google.Apis.Http.IHttpExecuteInterc
eptor"/> to the· |
43 /// <see cref="Google.Apis.Http.ConfigurableMessageHandler"/> execute interc
eptor list, which uses the given· | 43 /// <see cref="Google.Apis.Http.ConfigurableMessageHandler"/> execute interc
eptor list, which uses the given· |
44 /// Authenticator. It calls to its applying authentication method, and injec
ts the "Authorization" header in the· | 44 /// Authenticator. It calls to its applying authentication method, and injec
ts the "Authorization" header in the· |
45 /// request. | 45 /// request. |
46 /// If the given Authenticator implements <see cref="Google.Apis.Http.IUnsuc
cessfulReponseHandler"/>, this class | 46 /// If the given Authenticator implements <see cref="Google.Apis.Http.IHttpU
nsuccessfulResponseHandler"/>, this· |
47 /// adds the Authenticator to the <see cref="Google.Apis.Http.ConfigurableMe
ssageHandler"/>'s unsuccessful response | 47 /// class adds the Authenticator to the <see cref="Google.Apis.Http.Configur
ableMessageHandler"/>'s unsuccessful· |
48 /// handler list. | 48 /// response handler list. |
49 /// </summary> | 49 /// </summary> |
50 public abstract class BaseClientService : IClientService | 50 public abstract class BaseClientService : IClientService |
51 { | 51 { |
52 /// <summary> The class logger. </summary> | 52 /// <summary>The class logger.</summary> |
53 private static readonly ILogger Logger = ApplicationContext.Logger.ForTy
pe<BaseClientService>(); | 53 private static readonly ILogger Logger = ApplicationContext.Logger.ForTy
pe<BaseClientService>(); |
54 | 54 |
| 55 /// <summary>The default maximum allowed length of a URL string for GET
requests.</summary> |
55 [VisibleForTestOnly] | 56 [VisibleForTestOnly] |
56 /// <summary>The default maximum allowed length of a URL string for GET
requests.</summary> | |
57 internal const uint DefaultMaxUrlLength = 2048; | 57 internal const uint DefaultMaxUrlLength = 2048; |
58 | 58 |
59 #region Initializer | 59 #region Initializer |
60 | 60 |
61 /// <summary>· | 61 /// <summary>An initializer class for the client service.</summary> |
62 /// Indicates if exponential back-off is used automatically on exception
in a service request and\or when 503· | |
63 /// response is returned form the server. | |
64 /// </summary> | |
65 [Flags] | |
66 public enum ExponentialBackOffPolicy | |
67 { | |
68 None = 0, | |
69 Exception = 1, | |
70 UnsuccessfulResponse503 = 2 | |
71 } | |
72 | |
73 /// <summary> An initializer class for the client service. </summary> | |
74 public class Initializer | 62 public class Initializer |
75 { | 63 { |
76 /// <summary> | 64 /// <summary> |
77 /// Gets or sets the factory for creating <see cref="System.Net.Http
.HttpClient"/> instance. If this· | 65 /// Gets or sets the factory for creating <see cref="System.Net.Http
.HttpClient"/> instance. If this· |
78 /// property is not set the service uses a new <see cref="Google.Api
s.Http.HttpClientFactory"/> instance. | 66 /// property is not set the service uses a new <see cref="Google.Api
s.Http.HttpClientFactory"/> instance. |
79 /// </summary> | 67 /// </summary> |
80 public IHttpClientFactory HttpClientFactory { get; set; } | 68 public IHttpClientFactory HttpClientFactory { get; set; } |
81 | 69 |
82 /// <summary> | 70 /// <summary> |
83 /// Gets or sets an Http client initializer which is able to customi
ze properties on· | 71 /// Gets or sets a HTTP client initializer which is able to customiz
e properties on· |
84 /// <see cref="Google.Apis.Http.ConfigurableHttpClient"/> and· | 72 /// <see cref="Google.Apis.Http.ConfigurableHttpClient"/> and· |
85 /// <see cref="Google.Apis.Http.ConfigurableMessageHandler"/>. | 73 /// <see cref="Google.Apis.Http.ConfigurableMessageHandler"/>. |
86 /// </summary> | 74 /// </summary> |
87 public IConfigurableHttpClientInitializer HttpClientInitializer { ge
t; set; } | 75 public IConfigurableHttpClientInitializer HttpClientInitializer { ge
t; set; } |
88 | 76 |
89 /// <summary> | 77 /// <summary> |
90 /// Get or sets the exponential back-off policy used by the service.
Default value is· | 78 /// Get or sets the exponential back-off policy used by the service.
Default value is· |
91 /// <c>UnsuccessfulResponse503</c>, which means that exponential bac
k-off is used on 503 abnormal HTTP | 79 /// <c>UnsuccessfulResponse503</c>, which means that exponential bac
k-off is used on 503 abnormal HTTP |
92 /// response. | 80 /// response. |
93 /// If the value is set to <c>None</c>, no exponential back-off poli
cy is used, and it's up to user to | 81 /// If the value is set to <c>None</c>, no exponential back-off poli
cy is used, and it's up to user to |
94 /// configure the <seealso cref="Google.Apis.Http.ConfigurableMessag
eHandler"/> in an | 82 /// configure the <seealso cref="Google.Apis.Http.ConfigurableMessag
eHandler"/> in an |
95 /// <seealso cref="Google.Apis.Http.IConfigurableHttpClientInitializ
er"/> to set a specific back-off | 83 /// <seealso cref="Google.Apis.Http.IConfigurableHttpClientInitializ
er"/> to set a specific back-off |
96 /// implementation (using <seealso cref="Google.Api.Http.BackOffHand
ler"/>). | 84 /// implementation (using <seealso cref="Google.Apis.Http.BackOffHan
dler"/>). |
97 /// </summary> | 85 /// </summary> |
98 public ExponentialBackOffPolicy DefaultExponentialBackOffPolicy { ge
t; set; } | 86 public ExponentialBackOffPolicy DefaultExponentialBackOffPolicy { ge
t; set; } |
99 | 87 |
100 /// <summary> Gets or sets whether this service supports GZip. Defau
lt value is <c>true</c>. </summary> | 88 /// <summary>Gets or sets whether this service supports GZip. Defaul
t value is <c>true</c>.</summary> |
101 public bool GZipEnabled { get; set; } | 89 public bool GZipEnabled { get; set; } |
102 | 90 |
103 /// <summary> | 91 /// <summary> |
104 /// Gets and Sets the Serializer. Default value is <see cref="Google
.Apis.Json.NewtonsoftJsonSerializer"/>. | 92 /// Gets and Sets the serializer. Default value is <see cref="Google
.Apis.Json.NewtonsoftJsonSerializer"/>. |
105 /// </summary> | 93 /// </summary> |
106 public ISerializer Serializer { get; set; } | 94 public ISerializer Serializer { get; set; } |
107 | 95 |
108 /// <summary> Gets or sets the API Key. Default value is <c>null</c>
. </summary> | 96 /// <summary>Gets or sets the API Key. Default value is <c>null</c>.
</summary> |
109 public string ApiKey { get; set; } | 97 public string ApiKey { get; set; } |
110 | 98 |
111 /// <summary> | 99 /// <summary> |
112 /// Gets or sets the Authenticator. Default value is· | 100 /// Gets or sets the Authenticator. Default value is· |
113 /// <see cref="Google.Apis.Authentication.NullAuthenticator.Instance
"/>. | 101 /// <see cref="Google.Apis.Authentication.NullAuthenticator.Instance
"/>. |
114 /// </summary> | 102 /// </summary> |
115 public IAuthenticator Authenticator { get; set; } | 103 public IAuthenticator Authenticator { get; set; } |
116 | 104 |
117 /// <summary> | 105 /// <summary> |
118 /// Gets or sets Application name to be used in the User-Agent heade
r. Default value is <c>null</c>.· | 106 /// Gets or sets Application name to be used in the User-Agent heade
r. Default value is <c>null</c>.· |
119 /// </summary> | 107 /// </summary> |
120 public string ApplicationName { get; set; } | 108 public string ApplicationName { get; set; } |
121 | 109 |
122 /// <summary> | 110 /// <summary> |
123 /// Maximum allowed length of a URL string for GET requests. Default
value is <c>2048</c>. | 111 /// Maximum allowed length of a URL string for GET requests. Default
value is <c>2048</c>. If the value is |
| 112 /// set to <c>0</c>, requests will never be modified due to URL stri
ng length. |
124 /// </summary> | 113 /// </summary> |
125 public uint MaxUrlLength { get; set; } | 114 public uint MaxUrlLength { get; set; } |
126 | 115 |
127 /// <summary> Constructs a new initializer with default values. </su
mmary> | 116 /// <summary>Constructs a new initializer with default values.</summ
ary> |
128 public Initializer() | 117 public Initializer() |
129 { | 118 { |
130 GZipEnabled = true; | 119 GZipEnabled = true; |
131 Serializer = new NewtonsoftJsonSerializer(); | 120 Serializer = new NewtonsoftJsonSerializer(); |
132 Authenticator = NullAuthenticator.Instance; | 121 Authenticator = NullAuthenticator.Instance; |
133 DefaultExponentialBackOffPolicy = ExponentialBackOffPolicy.Unsuc
cessfulResponse503; | 122 DefaultExponentialBackOffPolicy = ExponentialBackOffPolicy.Unsuc
cessfulResponse503; |
134 MaxUrlLength = DefaultMaxUrlLength; | 123 MaxUrlLength = DefaultMaxUrlLength; |
135 } | 124 } |
136 } | 125 } |
137 | 126 |
138 /// <summary> | |
139 /// An initializer which adds exponential back-off as exception handler
and\or unsuccessful response handler by | |
140 /// the given <seealso cref="BaseClientService.ExponentialBackOffPolicy"
/>. | |
141 /// </summary> | |
142 private class ExponentialBackOffInitializer : IConfigurableHttpClientIni
tializer | |
143 { | |
144 private ExponentialBackOffPolicy Policy { get; set; } | |
145 private Func<BackOffHandler> CreateBackOff { get; set; } | |
146 | |
147 /// <summary> | |
148 /// Constructs a new back-off initializer with the given policy and
back-off handler create function. | |
149 /// </summary> | |
150 public ExponentialBackOffInitializer(ExponentialBackOffPolicy policy
, Func<BackOffHandler> createBackOff) | |
151 { | |
152 Policy = policy; | |
153 CreateBackOff = createBackOff; | |
154 } | |
155 | |
156 public void Initialize(ConfigurableHttpClient httpClient) | |
157 { | |
158 var backOff = CreateBackOff(); | |
159 | |
160 // add exception handler and\or unsuccessful response handler | |
161 if ((Policy & ExponentialBackOffPolicy.Exception) == Exponential
BackOffPolicy.Exception) | |
162 { | |
163 httpClient.MessageHandler.ExceptionHandlers.Add(backOff); | |
164 } | |
165 | |
166 if ((Policy & ExponentialBackOffPolicy.UnsuccessfulResponse503)
== | |
167 ExponentialBackOffPolicy.UnsuccessfulResponse503) | |
168 { | |
169 httpClient.MessageHandler.UnsuccessfulResponseHandlers.Add(b
ackOff); | |
170 } | |
171 } | |
172 } | |
173 | |
174 #endregion | 127 #endregion |
175 | 128 |
176 /// <summary> Constructs a new base client with the specified initialize
r. </summary> | 129 /// <summary>Constructs a new base client with the specified initializer
.</summary> |
177 protected BaseClientService(Initializer initializer) | 130 protected BaseClientService(Initializer initializer) |
178 { | 131 { |
179 // sets the right properties by the initializer's properties | 132 // Set the right properties by the initializer's properties. |
180 GZipEnabled = initializer.GZipEnabled; | 133 GZipEnabled = initializer.GZipEnabled; |
181 Serializer = initializer.Serializer; | 134 Serializer = initializer.Serializer; |
182 ApiKey = initializer.ApiKey; | 135 ApiKey = initializer.ApiKey; |
183 Authenticator = initializer.Authenticator; | 136 Authenticator = initializer.Authenticator; |
184 ApplicationName = initializer.ApplicationName; | 137 ApplicationName = initializer.ApplicationName; |
185 if (ApplicationName == null) | 138 if (ApplicationName == null) |
186 { | 139 { |
187 Logger.Warning("Application name is not set. Please set Initiali
zer.ApplicationName property"); | 140 Logger.Warning("Application name is not set. Please set Initiali
zer.ApplicationName property"); |
188 } | 141 } |
189 HttpClientInitializer = initializer.HttpClientInitializer; | 142 HttpClientInitializer = initializer.HttpClientInitializer; |
190 | 143 |
191 // create an Http client for this service | 144 // Create a HTTP client for this service. |
192 HttpClient = CreateHttpClient(initializer); | 145 HttpClient = CreateHttpClient(initializer); |
193 } | 146 } |
194 | 147 |
195 /// <summary> Return <c>true</c> if this service contains the specified
feature. </summary> | 148 /// <summary>Returns <c>true</c> if this service contains the specified
feature.</summary> |
196 private bool HasFeature(Features feature) | 149 private bool HasFeature(Features feature) |
197 { | 150 { |
198 return Features.Contains(feature.GetStringValue()); | 151 return Features.Contains(feature.GetStringValue()); |
199 } | 152 } |
200 | 153 |
201 private ConfigurableHttpClient CreateHttpClient(Initializer initializer) | 154 private ConfigurableHttpClient CreateHttpClient(Initializer initializer) |
202 { | 155 { |
203 // if factory wasn't set use the default Http client factory | 156 // If factory wasn't set use the default HTTP client factory. |
204 var factory = initializer.HttpClientFactory ?? new HttpClientFactory
(); | 157 var factory = initializer.HttpClientFactory ?? new HttpClientFactory
(); |
205 var args = new CreateHttpClientArgs | 158 var args = new CreateHttpClientArgs |
206 { | 159 { |
207 GZipEnabled = GZipEnabled, | 160 GZipEnabled = GZipEnabled, |
208 ApplicationName = ApplicationName, | 161 ApplicationName = ApplicationName, |
209 }; | 162 }; |
210 | 163 |
211 // add the user's input initializer | 164 // Add the user's input initializer. |
212 if (HttpClientInitializer != null) | 165 if (HttpClientInitializer != null) |
213 { | 166 { |
214 args.Initializers.Add(HttpClientInitializer); | 167 args.Initializers.Add(HttpClientInitializer); |
215 } | 168 } |
216 | 169 |
217 // add exponential back-off initializer if necessary | 170 // Add exponential back-off initializer if necessary. |
218 if (initializer.DefaultExponentialBackOffPolicy != ExponentialBackOf
fPolicy.None) | 171 if (initializer.DefaultExponentialBackOffPolicy != ExponentialBackOf
fPolicy.None) |
219 { | 172 { |
220 args.Initializers.Add(new ExponentialBackOffInitializer(initiali
zer.DefaultExponentialBackOffPolicy, | 173 args.Initializers.Add(new ExponentialBackOffInitializer(initiali
zer.DefaultExponentialBackOffPolicy, |
221 CreateBackOffHandler)); | 174 CreateBackOffHandler)); |
222 } | 175 } |
223 | 176 |
224 // add authenticator initializer to intercept a request and add the
"Authorization" header and also handle | 177 // Add authenticator initializer to intercept a request and add the
"Authorization" header and also handle |
225 // abnormal 401 responses in case the authenticator is an instance o
f unsuccessful response handler. | 178 // abnormal 401 responses in case the authenticator is an instance o
f unsuccessful response handler. |
226 args.Initializers.Add(new AuthenticatorMessageHandlerInitializer(Aut
henticator)); | 179 args.Initializers.Add(new AuthenticatorMessageHandlerInitializer(Aut
henticator)); |
227 ············ | 180 ············ |
228 var httpClient = factory.CreateHttpClient(args); | 181 var httpClient = factory.CreateHttpClient(args); |
229 httpClient.MessageHandler.ExecuteInterceptors.Add(new MaxUrlLengthIn
terceptor(initializer.MaxUrlLength)); | 182 if (initializer.MaxUrlLength > 0) |
| 183 { |
| 184 httpClient.MessageHandler.ExecuteInterceptors.Add( |
| 185 new MaxUrlLengthInterceptor(initializer.MaxUrlLength)); |
| 186 } |
230 return httpClient; | 187 return httpClient; |
231 } | 188 } |
232 | 189 |
233 /// <summary> | 190 /// <summary> |
234 /// Creates the back-off handler with <seealso cref="Google.Apis.Util.Ex
ponentialBackOff"/>.· | 191 /// Creates the back-off handler with <seealso cref="Google.Apis.Util.Ex
ponentialBackOff"/>.· |
235 /// Overrides this method to change the default behavior of back-off han
dler (e.g. you can change the maximum | 192 /// Overrides this method to change the default behavior of back-off han
dler (e.g. you can change the maximum |
236 /// waited request's time span, or create a back-off handler with you ow
n implementation of· | 193 /// waited request's time span, or create a back-off handler with you ow
n implementation of· |
237 /// <seealso cref="Google.Apis.Util.IBackOff"/>). | 194 /// <seealso cref="Google.Apis.Util.IBackOff"/>). |
238 /// </summary> | 195 /// </summary> |
239 protected virtual BackOffHandler CreateBackOffHandler() | 196 protected virtual BackOffHandler CreateBackOffHandler() |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
290 public ISerializer Serializer { get; private set; } | 247 public ISerializer Serializer { get; private set; } |
291 | 248 |
292 public virtual string SerializeObject(object obj) | 249 public virtual string SerializeObject(object obj) |
293 { | 250 { |
294 if (HasFeature(Discovery.Features.LegacyDataResponse)) | 251 if (HasFeature(Discovery.Features.LegacyDataResponse)) |
295 { | 252 { |
296 // Legacy path | 253 // Legacy path |
297 var request = new StandardResponse<object> { Data = obj }; | 254 var request = new StandardResponse<object> { Data = obj }; |
298 return Serializer.Serialize(request); | 255 return Serializer.Serialize(request); |
299 } | 256 } |
300 | |
301 // New v1.0 path | |
302 return Serializer.Serialize(obj); | 257 return Serializer.Serialize(obj); |
303 } | 258 } |
304 | 259 |
305 public virtual async Task<T> DeserializeResponse<T>(HttpResponseMessage
response) | 260 public virtual async Task<T> DeserializeResponse<T>(HttpResponseMessage
response) |
306 { | 261 { |
307 var text = await response.Content.ReadAsStringAsync().ConfigureAwait
(false); | 262 var text = await response.Content.ReadAsStringAsync().ConfigureAwait
(false); |
308 | 263 |
309 // If a string is request, don't parse the response. | 264 // If a string is request, don't parse the response. |
310 if (typeof(T).Equals(typeof(string))) | 265 if (typeof(T).Equals(typeof(string))) |
311 { | 266 { |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
380 // Standard response (which contains data and error properties) | 335 // Standard response (which contains data and error properties) |
381 throw new GoogleApiException(Name, | 336 throw new GoogleApiException(Name, |
382 "An Error occurred, but the error response could not be dese
rialized", ex); | 337 "An Error occurred, but the error response could not be dese
rialized", ex); |
383 } | 338 } |
384 | 339 |
385 return errorResponse.Error; | 340 return errorResponse.Error; |
386 } | 341 } |
387 | 342 |
388 #endregion | 343 #endregion |
389 | 344 |
390 #region Abstract Memebrs | 345 #region Abstract Members |
391 | 346 |
392 public abstract string Name { get; } | 347 public abstract string Name { get; } |
393 public abstract string BaseUri { get; } | 348 public abstract string BaseUri { get; } |
394 public abstract string BasePath { get; } | 349 public abstract string BasePath { get; } |
395 | 350 |
396 public abstract IList<string> Features { get; } | 351 public abstract IList<string> Features { get; } |
397 | 352 |
398 #endregion | 353 #endregion |
399 | 354 |
400 #endregion | 355 #endregion |
401 | 356 |
402 /// <summary> Creates a GZip stream by the given serialized object. </su
mmary> | 357 /// <summary>Creates a GZip stream by the given serialized object.</summ
ary> |
403 private static Stream CreateGZipStream(string serializedObject) | 358 private static Stream CreateGZipStream(string serializedObject) |
404 { | 359 { |
405 byte[] bytes = System.Text.Encoding.UTF8.GetBytes(serializedObject); | 360 byte[] bytes = System.Text.Encoding.UTF8.GetBytes(serializedObject); |
406 using (System.IO.MemoryStream ms = new System.IO.MemoryStream()) | 361 using (System.IO.MemoryStream ms = new System.IO.MemoryStream()) |
407 { | 362 { |
408 using (GZipStream gzip = new GZipStream(ms, CompressionMode.Comp
ress, true)) | 363 using (GZipStream gzip = new GZipStream(ms, CompressionMode.Comp
ress, true)) |
409 { | 364 { |
410 gzip.Write(bytes, 0, bytes.Length); | 365 gzip.Write(bytes, 0, bytes.Length); |
411 } | 366 } |
412 | 367 |
413 // reset the stream to the beginning. It doesn't work otherwise! | 368 // reset the stream to the beginning. It doesn't work otherwise! |
414 ms.Position = 0; | 369 ms.Position = 0; |
415 byte[] compressed = new byte[ms.Length]; | 370 byte[] compressed = new byte[ms.Length]; |
416 ms.Read(compressed, 0, compressed.Length); | 371 ms.Read(compressed, 0, compressed.Length); |
417 return new MemoryStream(compressed); | 372 return new MemoryStream(compressed); |
418 } | 373 } |
419 } | 374 } |
420 | 375 |
421 public virtual void Dispose() | 376 public virtual void Dispose() |
422 { | 377 { |
423 if (HttpClient != null) | 378 if (HttpClient != null) |
424 { | 379 { |
425 HttpClient.Dispose(); | 380 HttpClient.Dispose(); |
426 } | 381 } |
427 } | 382 } |
428 } | 383 } |
429 | |
430 [VisibleForTestOnly] | |
431 /// <summary> | |
432 /// Intercepts HTTP GET requests with a URLs longer than <see cref="Initiali
zer.MaxUrlLength" />. | |
433 /// The interceptor will change such requests as follows:· | |
434 /// <list type="bullet"> | |
435 /// <item>The request's method will be changed to POST</item> | |
436 /// <item>A <c>X-HTTP-Method-Override</c> header will be added with the valu
e <c>GET</c></item> | |
437 /// <item>Any query parameters from the URI will be moved into the body of t
he request. | |
438 /// <item>If query parameters are moved, the content type is set to <c>appli
cation/x-www-form-urlencoded</c></item> | |
439 /// </item></list> | |
440 /// </summary> | |
441 internal class MaxUrlLengthInterceptor : IHttpExecuteInterceptor· | |
442 { | |
443 private readonly uint maxUrlLength; | |
444 | |
445 ///<summary>Constructs a new Max URL length interceptor with the given m
ax length.</summary> | |
446 public MaxUrlLengthInterceptor(uint maxUrlLength) | |
447 { | |
448 this.maxUrlLength = maxUrlLength; | |
449 } | |
450 | |
451 public void Intercept(HttpRequestMessage request) | |
452 { | |
453 if (request.Method != HttpMethod.Get || request.RequestUri.AbsoluteU
ri.Length <= maxUrlLength) | |
454 { | |
455 return; | |
456 } | |
457 // Change the method to POST | |
458 request.Method = HttpMethod.Post; | |
459 var query = request.RequestUri.Query; | |
460 if (!String.IsNullOrEmpty(query)) | |
461 { | |
462 // Move query parameters to the body (without the "?") | |
463 request.Content = new StringContent(query.Substring(1)); | |
464 request.Content.Headers.ContentType = new MediaTypeHeaderValue("
application/x-www-form-urlencoded"); | |
465 var requestString = request.RequestUri.ToString(); | |
466 request.RequestUri = new Uri(requestString.Remove(requestString.
IndexOf("?") - 1)); | |
467 } | |
468 request.Headers.Add("X-HTTP-Method-Override", "GET"); | |
469 } | |
470 } | |
471 } | 384 } |
LEFT | RIGHT |