PageRenderTime 45ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/src/System.Web.Http/HttpRequestMessageExtensions.cs

https://bitbucket.org/lajjne/aspnetwebstack
C# | 643 lines | 337 code | 63 blank | 243 comment | 69 complexity | 11e98db9b08a7a057fbe469bb46a3709 MD5 | raw file
Possible License(s): Apache-2.0
  1. // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Diagnostics.CodeAnalysis;
  5. using System.Linq;
  6. using System.Net.Http.Formatting;
  7. using System.Net.Http.Headers;
  8. using System.Security.Cryptography.X509Certificates;
  9. using System.Threading;
  10. using System.Web.Http;
  11. using System.Web.Http.Dependencies;
  12. using System.Web.Http.Hosting;
  13. using System.Web.Http.ModelBinding;
  14. using System.Web.Http.Properties;
  15. using System.Web.Http.Routing;
  16. namespace System.Net.Http
  17. {
  18. /// <summary>
  19. /// Provides extension methods for the <see cref="HttpRequestMessage"/> class.
  20. /// </summary>
  21. [EditorBrowsable(EditorBrowsableState.Never)]
  22. public static class HttpRequestMessageExtensions
  23. {
  24. /// <summary>
  25. /// Gets the <see cref="HttpConfiguration"/> for the given request.
  26. /// </summary>
  27. /// <param name="request">The HTTP request.</param>
  28. /// <returns>The <see cref="HttpConfiguration"/>.</returns>
  29. public static HttpConfiguration GetConfiguration(this HttpRequestMessage request)
  30. {
  31. if (request == null)
  32. {
  33. throw Error.ArgumentNull("request");
  34. }
  35. return request.GetProperty<HttpConfiguration>(HttpPropertyKeys.HttpConfigurationKey);
  36. }
  37. /// <summary>
  38. /// Gets the dependency resolver scope associated with this <see cref="HttpRequestMessage"/>.
  39. /// Services which are retrieved from this scope will be released when the request is
  40. /// cleaned up by the framework.
  41. /// </summary>
  42. /// <param name="request">The HTTP request.</param>
  43. /// <returns>The <see cref="IDependencyScope"/> for the given request.</returns>
  44. public static IDependencyScope GetDependencyScope(this HttpRequestMessage request)
  45. {
  46. if (request == null)
  47. {
  48. throw Error.ArgumentNull("request");
  49. }
  50. IDependencyScope result;
  51. if (!request.Properties.TryGetValue<IDependencyScope>(HttpPropertyKeys.DependencyScope, out result))
  52. {
  53. IDependencyResolver dependencyResolver = request.GetConfiguration().DependencyResolver;
  54. result = dependencyResolver.BeginScope();
  55. if (result == null)
  56. {
  57. throw Error.InvalidOperation(SRResources.DependencyResolver_BeginScopeReturnsNull, dependencyResolver.GetType().Name);
  58. }
  59. request.Properties[HttpPropertyKeys.DependencyScope] = result;
  60. request.RegisterForDispose(result);
  61. }
  62. return result;
  63. }
  64. /// <summary>
  65. /// Gets the <see cref="System.Threading.SynchronizationContext"/> for the given request or null if not available.
  66. /// </summary>
  67. /// <param name="request">The HTTP request.</param>
  68. /// <returns>The <see cref="System.Threading.SynchronizationContext"/> or null.</returns>
  69. public static SynchronizationContext GetSynchronizationContext(this HttpRequestMessage request)
  70. {
  71. if (request == null)
  72. {
  73. throw Error.ArgumentNull("request");
  74. }
  75. return request.GetProperty<SynchronizationContext>(HttpPropertyKeys.SynchronizationContextKey);
  76. }
  77. /// <summary>
  78. /// Gets the current <see cref="T:System.Security.Cryptography.X509Certificates.X509Certificate2"/> or null if not available.
  79. /// </summary>
  80. /// <param name="request">The HTTP request.</param>
  81. /// <returns>The <see cref="T:System.Security.Cryptography.X509Certificates.X509Certificate2"/> or null.</returns>
  82. public static X509Certificate2 GetClientCertificate(this HttpRequestMessage request)
  83. {
  84. if (request == null)
  85. {
  86. throw Error.ArgumentNull("request");
  87. }
  88. X509Certificate2 result = null;
  89. if (!request.Properties.TryGetValue(HttpPropertyKeys.ClientCertificateKey, out result))
  90. {
  91. // now let us get out the delegate and try to invoke it
  92. Func<HttpRequestMessage, X509Certificate2> retrieveCertificate;
  93. if (request.Properties.TryGetValue(HttpPropertyKeys.RetrieveClientCertificateDelegateKey, out retrieveCertificate))
  94. {
  95. result = retrieveCertificate(request);
  96. if (result != null)
  97. {
  98. request.Properties.Add(HttpPropertyKeys.ClientCertificateKey, result);
  99. }
  100. }
  101. }
  102. return result;
  103. }
  104. /// <summary>
  105. /// Gets the <see cref="System.Web.Http.Routing.IHttpRouteData"/> for the given request or null if not available.
  106. /// </summary>
  107. /// <param name="request">The HTTP request.</param>
  108. /// <returns>The <see cref="System.Web.Http.Routing.IHttpRouteData"/> or null.</returns>
  109. public static IHttpRouteData GetRouteData(this HttpRequestMessage request)
  110. {
  111. if (request == null)
  112. {
  113. throw Error.ArgumentNull("request");
  114. }
  115. return request.GetProperty<IHttpRouteData>(HttpPropertyKeys.HttpRouteDataKey);
  116. }
  117. private static T GetProperty<T>(this HttpRequestMessage request, string key)
  118. {
  119. T value;
  120. request.Properties.TryGetValue(key, out value);
  121. return value;
  122. }
  123. /// <summary>
  124. /// Helper method for creating an <see cref="HttpResponseMessage"/> message with a "416 (Requested Range Not Satisfiable)" status code.
  125. /// This response can be used in combination with the <see cref="ByteRangeStreamContent"/> to indicate that the requested range or
  126. /// ranges do not overlap with the current resource. The response contains a "Content-Range" header indicating the valid upper and lower
  127. /// bounds for requested ranges.
  128. /// </summary>
  129. /// <param name="request">The request.</param>
  130. /// <param name="invalidByteRangeException">An <see cref="InvalidByteRangeException"/> instance, typically thrown by a
  131. /// <see cref="ByteRangeStreamContent"/> instance.</param>
  132. /// <returns>An 416 (Requested Range Not Satisfiable) error response with a Content-Range header indicating the valid range.</returns>
  133. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Satisfiable", Justification = "Word is correctly spelled.")]
  134. [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Caller will dispose")]
  135. public static HttpResponseMessage CreateErrorResponse(this HttpRequestMessage request, InvalidByteRangeException invalidByteRangeException)
  136. {
  137. if (invalidByteRangeException == null)
  138. {
  139. throw Error.ArgumentNull("invalidByteRangeException");
  140. }
  141. HttpResponseMessage rangeNotSatisfiableResponse = request.CreateErrorResponse(HttpStatusCode.RequestedRangeNotSatisfiable, invalidByteRangeException);
  142. rangeNotSatisfiableResponse.Content.Headers.ContentRange = invalidByteRangeException.ContentRange;
  143. return rangeNotSatisfiableResponse;
  144. }
  145. /// <summary>
  146. /// Helper method that performs content negotiation and creates a <see cref="HttpResponseMessage"/> representing an error
  147. /// with an instance of <see cref="ObjectContent{T}"/> wrapping an <see cref="HttpError"/> with message <paramref name="message"/>.
  148. /// If no formatter is found, this method returns a response with status 406 NotAcceptable.
  149. /// </summary>
  150. /// <remarks>
  151. /// This method requires that <paramref name="request"/> has been associated with an instance of
  152. /// <see cref="HttpConfiguration"/>.
  153. /// </remarks>
  154. /// <param name="request">The request.</param>
  155. /// <param name="statusCode">The status code of the created response.</param>
  156. /// <param name="message">The error message.</param>
  157. /// <returns>An error response with error message <paramref name="message"/> and status code <paramref name="statusCode"/>.</returns>
  158. public static HttpResponseMessage CreateErrorResponse(this HttpRequestMessage request, HttpStatusCode statusCode, string message)
  159. {
  160. return request.CreateErrorResponse(statusCode, new HttpError(message));
  161. }
  162. /// <summary>
  163. /// Helper method that performs content negotiation and creates a <see cref="HttpResponseMessage"/> representing an error
  164. /// with an instance of <see cref="ObjectContent{T}"/> wrapping an <see cref="HttpError"/> with message <paramref name="message"/>
  165. /// and message detail <paramref name="messageDetail"/>.If no formatter is found, this method returns a response with
  166. /// status 406 NotAcceptable.
  167. /// </summary>
  168. /// <remarks>
  169. /// This method requires that <paramref name="request"/> has been associated with an instance of
  170. /// <see cref="HttpConfiguration"/>.
  171. /// </remarks>
  172. /// <param name="request">The request.</param>
  173. /// <param name="statusCode">The status code of the created response.</param>
  174. /// <param name="message">The error message. This message will always be seen by clients.</param>
  175. /// <param name="messageDetail">The error message detail. This message will only be seen by clients if we should include error detail.</param>
  176. /// <returns>An error response with error message <paramref name="message"/> and message detail <paramref name="messageDetail"/>
  177. /// and status code <paramref name="statusCode"/>.</returns>
  178. internal static HttpResponseMessage CreateErrorResponse(this HttpRequestMessage request, HttpStatusCode statusCode, string message, string messageDetail)
  179. {
  180. return request.CreateErrorResponse(statusCode, includeErrorDetail => includeErrorDetail ? new HttpError(message, messageDetail) : new HttpError(message));
  181. }
  182. /// <summary>
  183. /// Helper method that performs content negotiation and creates a <see cref="HttpResponseMessage"/> representing an error
  184. /// with an instance of <see cref="ObjectContent{T}"/> wrapping an <see cref="HttpError"/> with error message <paramref name="message"/>
  185. /// for exception <paramref name="exception"/>. If no formatter is found, this method returns a response with status 406 NotAcceptable.
  186. /// </summary>
  187. /// <remarks>
  188. /// This method requires that <paramref name="request"/> has been associated with an instance of
  189. /// <see cref="HttpConfiguration"/>.
  190. /// </remarks>
  191. /// <param name="request">The request.</param>
  192. /// <param name="statusCode">The status code of the created response.</param>
  193. /// <param name="message">The error message.</param>
  194. /// <param name="exception">The exception.</param>
  195. /// <returns>An error response for <paramref name="exception"/> with error message <paramref name="message"/>
  196. /// and status code <paramref name="statusCode"/>.</returns>
  197. public static HttpResponseMessage CreateErrorResponse(this HttpRequestMessage request, HttpStatusCode statusCode, string message, Exception exception)
  198. {
  199. if (request == null)
  200. {
  201. throw Error.ArgumentNull("request");
  202. }
  203. return request.CreateErrorResponse(statusCode, includeErrorDetail => new HttpError(exception, includeErrorDetail) { Message = message });
  204. }
  205. /// <summary>
  206. /// Helper method that performs content negotiation and creates a <see cref="HttpResponseMessage"/> representing an error
  207. /// with an instance of <see cref="ObjectContent{T}"/> wrapping an <see cref="HttpError"/> for exception <paramref name="exception"/>.
  208. /// If no formatter is found, this method returns a response with status 406 NotAcceptable.
  209. /// </summary>
  210. /// <remarks>
  211. /// This method requires that <paramref name="request"/> has been associated with an instance of
  212. /// <see cref="HttpConfiguration"/>.
  213. /// </remarks>
  214. /// <param name="request">The request.</param>
  215. /// <param name="statusCode">The status code of the created response.</param>
  216. /// <param name="exception">The exception.</param>
  217. /// <returns>An error response for <paramref name="exception"/> with status code <paramref name="statusCode"/>.</returns>
  218. public static HttpResponseMessage CreateErrorResponse(this HttpRequestMessage request, HttpStatusCode statusCode, Exception exception)
  219. {
  220. if (request == null)
  221. {
  222. throw Error.ArgumentNull("request");
  223. }
  224. return request.CreateErrorResponse(statusCode, includeErrorDetail => new HttpError(exception, includeErrorDetail));
  225. }
  226. /// <summary>
  227. /// Helper method that performs content negotiation and creates a <see cref="HttpResponseMessage"/> representing an error
  228. /// with an instance of <see cref="ObjectContent{T}"/> wrapping an <see cref="HttpError"/> for model state <paramref name="modelState"/>.
  229. /// If no formatter is found, this method returns a response with status 406 NotAcceptable.
  230. /// </summary>
  231. /// <remarks>
  232. /// This method requires that <paramref name="request"/> has been associated with an instance of
  233. /// <see cref="HttpConfiguration"/>.
  234. /// </remarks>
  235. /// <param name="request">The request.</param>
  236. /// <param name="statusCode">The status code of the created response.</param>
  237. /// <param name="modelState">The model state.</param>
  238. /// <returns>An error response for <paramref name="modelState"/> with status code <paramref name="statusCode"/>.</returns>
  239. public static HttpResponseMessage CreateErrorResponse(this HttpRequestMessage request, HttpStatusCode statusCode, ModelStateDictionary modelState)
  240. {
  241. if (request == null)
  242. {
  243. throw Error.ArgumentNull("request");
  244. }
  245. return request.CreateErrorResponse(statusCode, includeErrorDetail => new HttpError(modelState, includeErrorDetail));
  246. }
  247. /// <summary>
  248. /// Helper method that performs content negotiation and creates a <see cref="HttpResponseMessage"/> representing an error
  249. /// with an instance of <see cref="ObjectContent{T}"/> wrapping <paramref name="error"/> as the content. If no formatter
  250. /// is found, this method returns a response with status 406 NotAcceptable.
  251. /// </summary>
  252. /// <remarks>
  253. /// This method requires that <paramref name="request"/> has been associated with an instance of
  254. /// <see cref="HttpConfiguration"/>.
  255. /// </remarks>
  256. /// <param name="request">The request.</param>
  257. /// <param name="statusCode">The status code of the created response.</param>
  258. /// <param name="error">The error to wrap.</param>
  259. /// <returns>An error response wrapping <paramref name="error"/> with status code <paramref name="statusCode"/>.</returns>
  260. public static HttpResponseMessage CreateErrorResponse(this HttpRequestMessage request, HttpStatusCode statusCode, HttpError error)
  261. {
  262. if (request == null)
  263. {
  264. throw Error.ArgumentNull("request");
  265. }
  266. return request.CreateErrorResponse(statusCode, includeErrorDetail => error);
  267. }
  268. private static HttpResponseMessage CreateErrorResponse(this HttpRequestMessage request, HttpStatusCode statusCode, Func<bool, HttpError> errorCreator)
  269. {
  270. HttpConfiguration configuration = request.GetConfiguration();
  271. // CreateErrorResponse should never fail, even if there is no configuration associated with the request
  272. // In that case, use the default HttpConfiguration to con-neg the response media type
  273. if (configuration == null)
  274. {
  275. using (HttpConfiguration defaultConfig = new HttpConfiguration())
  276. {
  277. HttpError error = errorCreator(defaultConfig.ShouldIncludeErrorDetail(request));
  278. return request.CreateResponse<HttpError>(statusCode, error, defaultConfig);
  279. }
  280. }
  281. else
  282. {
  283. HttpError error = errorCreator(configuration.ShouldIncludeErrorDetail(request));
  284. return request.CreateResponse<HttpError>(statusCode, error, configuration);
  285. }
  286. }
  287. /// <summary>
  288. /// Helper method that performs content negotiation and creates a <see cref="HttpResponseMessage"/> with an instance
  289. /// of <see cref="ObjectContent{T}"/> as the content if a formatter can be found. If no formatter is found, this
  290. /// method returns a response with status 406 NotAcceptable. This forwards the call to
  291. /// <see cref="CreateResponse{T}(HttpRequestMessage, HttpStatusCode, T, HttpConfiguration)"/> with a <c>null</c>
  292. /// configuration.
  293. /// </summary>
  294. /// <remarks>
  295. /// This method requires that <paramref name="request"/> has been associated with an instance of
  296. /// <see cref="HttpConfiguration"/>.
  297. /// </remarks>
  298. /// <typeparam name="T">The type of the value.</typeparam>
  299. /// <param name="request">The request.</param>
  300. /// <param name="statusCode">The status code of the created response.</param>
  301. /// <param name="value">The value to wrap. Can be <c>null</c>.</param>
  302. /// <returns>A response wrapping <paramref name="value"/> with <paramref name="statusCode"/>.</returns>
  303. public static HttpResponseMessage CreateResponse<T>(this HttpRequestMessage request, HttpStatusCode statusCode, T value)
  304. {
  305. return request.CreateResponse<T>(statusCode, value, configuration: null);
  306. }
  307. /// <summary>
  308. /// Helper method that performs content negotiation and creates a <see cref="HttpResponseMessage"/> with an instance
  309. /// of <see cref="ObjectContent{T}"/> as the content if a formatter can be found. If no formatter is found, this
  310. /// method returns a response with status 406 NotAcceptable.
  311. /// </summary>
  312. /// <remarks>
  313. /// This method will use the provided <paramref name="configuration"/> or it will get the
  314. /// <see cref="HttpConfiguration"/> instance associated with <paramref name="request"/>.
  315. /// </remarks>
  316. /// <typeparam name="T">The type of the value.</typeparam>
  317. /// <param name="request">The request.</param>
  318. /// <param name="statusCode">The status code of the created response.</param>
  319. /// <param name="value">The value to wrap. Can be <c>null</c>.</param>
  320. /// <param name="configuration">The configuration to use. Can be <c>null</c>.</param>
  321. /// <returns>A response wrapping <paramref name="value"/> with <paramref name="statusCode"/>.</returns>
  322. [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Caller will dispose")]
  323. public static HttpResponseMessage CreateResponse<T>(this HttpRequestMessage request, HttpStatusCode statusCode, T value, HttpConfiguration configuration)
  324. {
  325. if (request == null)
  326. {
  327. throw Error.ArgumentNull("request");
  328. }
  329. configuration = configuration ?? request.GetConfiguration();
  330. if (configuration == null)
  331. {
  332. throw Error.InvalidOperation(SRResources.HttpRequestMessageExtensions_NoConfiguration);
  333. }
  334. IContentNegotiator contentNegotiator = configuration.Services.GetContentNegotiator();
  335. if (contentNegotiator == null)
  336. {
  337. throw Error.InvalidOperation(SRResources.HttpRequestMessageExtensions_NoContentNegotiator, typeof(IContentNegotiator).FullName);
  338. }
  339. IEnumerable<MediaTypeFormatter> formatters = configuration.Formatters;
  340. // Run content negotiation
  341. ContentNegotiationResult result = contentNegotiator.Negotiate(typeof(T), request, formatters);
  342. if (result == null)
  343. {
  344. // no result from content negotiation indicates that 406 should be sent.
  345. return new HttpResponseMessage
  346. {
  347. StatusCode = HttpStatusCode.NotAcceptable,
  348. RequestMessage = request,
  349. };
  350. }
  351. else
  352. {
  353. MediaTypeHeaderValue mediaType = result.MediaType;
  354. return new HttpResponseMessage
  355. {
  356. // At this point mediaType should be a cloned value (the content negotiator is responsible for returning a new copy)
  357. Content = new ObjectContent<T>(value, result.Formatter, mediaType),
  358. StatusCode = statusCode,
  359. RequestMessage = request
  360. };
  361. }
  362. }
  363. /// <summary>
  364. /// Helper method that creates a <see cref="HttpResponseMessage"/> with an <see cref="ObjectContent{T}"/> instance containing the provided
  365. /// <paramref name="value"/>. The given <paramref name="mediaType"/> is used to find an instance of <see cref="MediaTypeFormatter"/>.
  366. /// </summary>
  367. /// <typeparam name="T">The type of the value.</typeparam>
  368. /// <param name="request">The request.</param>
  369. /// <param name="statusCode">The status code of the created response.</param>
  370. /// <param name="value">The value to wrap. Can be <c>null</c>.</param>
  371. /// <param name="mediaType">The media type used to look up an instance of <see cref="MediaTypeFormatter"/>.</param>
  372. /// <exception cref="InvalidOperationException">Thrown if the <paramref name="request"/> does not have an associated
  373. /// <see cref="HttpConfiguration"/> instance or if the configuration does not have a formatter matching <paramref name="mediaType"/>.</exception>
  374. /// <returns>A response wrapping <paramref name="value"/> with <paramref name="statusCode"/>.</returns>
  375. public static HttpResponseMessage CreateResponse<T>(this HttpRequestMessage request, HttpStatusCode statusCode, T value, string mediaType)
  376. {
  377. return request.CreateResponse(statusCode, value, new MediaTypeHeaderValue(mediaType));
  378. }
  379. /// <summary>
  380. /// Helper method that creates a <see cref="HttpResponseMessage"/> with an <see cref="ObjectContent{T}"/> instance containing the provided
  381. /// <paramref name="value"/>. The given <paramref name="mediaType"/> is used to find an instance of <see cref="MediaTypeFormatter"/>.
  382. /// </summary>
  383. /// <typeparam name="T">The type of the value.</typeparam>
  384. /// <param name="request">The request.</param>
  385. /// <param name="statusCode">The status code of the created response.</param>
  386. /// <param name="value">The value to wrap. Can be <c>null</c>.</param>
  387. /// <param name="mediaType">The media type used to look up an instance of <see cref="MediaTypeFormatter"/>.</param>
  388. /// <exception cref="InvalidOperationException">Thrown if the <paramref name="request"/> does not have an associated
  389. /// <see cref="HttpConfiguration"/> instance or if the configuration does not have a formatter matching <paramref name="mediaType"/>.</exception>
  390. /// <returns>A response wrapping <paramref name="value"/> with <paramref name="statusCode"/>.</returns>
  391. public static HttpResponseMessage CreateResponse<T>(this HttpRequestMessage request, HttpStatusCode statusCode, T value, MediaTypeHeaderValue mediaType)
  392. {
  393. if (request == null)
  394. {
  395. throw Error.ArgumentNull("request");
  396. }
  397. if (mediaType == null)
  398. {
  399. throw Error.ArgumentNull("mediaType");
  400. }
  401. HttpConfiguration configuration = request.GetConfiguration();
  402. if (configuration == null)
  403. {
  404. throw Error.InvalidOperation(SRResources.HttpRequestMessageExtensions_NoConfiguration);
  405. }
  406. MediaTypeFormatter formatter = configuration.Formatters.FindWriter(typeof(T), mediaType);
  407. if (formatter == null)
  408. {
  409. throw Error.InvalidOperation(SRResources.HttpRequestMessageExtensions_NoMatchingFormatter, mediaType, typeof(T).Name);
  410. }
  411. return request.CreateResponse(statusCode, value, formatter, mediaType);
  412. }
  413. /// <summary>
  414. /// Helper method that creates a <see cref="HttpResponseMessage"/> with an <see cref="ObjectContent{T}"/> instance containing the provided
  415. /// <paramref name="value"/> and the given <paramref name="formatter"/>.
  416. /// </summary>
  417. /// <typeparam name="T">The type of the value.</typeparam>
  418. /// <param name="request">The request.</param>
  419. /// <param name="statusCode">The status code of the created response.</param>
  420. /// <param name="value">The value to wrap. Can be <c>null</c>.</param>
  421. /// <param name="formatter">The formatter to use.</param>
  422. /// <returns>A response wrapping <paramref name="value"/> with <paramref name="statusCode"/>.</returns>
  423. public static HttpResponseMessage CreateResponse<T>(this HttpRequestMessage request, HttpStatusCode statusCode, T value, MediaTypeFormatter formatter)
  424. {
  425. return request.CreateResponse(statusCode, value, formatter, (MediaTypeHeaderValue)null);
  426. }
  427. /// <summary>
  428. /// Helper method that creates a <see cref="HttpResponseMessage"/> with an <see cref="ObjectContent{T}"/> instance containing the provided
  429. /// <paramref name="value"/> and the given <paramref name="formatter"/>.
  430. /// </summary>
  431. /// <typeparam name="T">The type of the value.</typeparam>
  432. /// <param name="request">The request.</param>
  433. /// <param name="statusCode">The status code of the created response.</param>
  434. /// <param name="value">The value to wrap. Can be <c>null</c>.</param>
  435. /// <param name="formatter">The formatter to use.</param>
  436. /// <param name="mediaType">The media type override to set on the response's content. Can be <c>null</c>.</param>
  437. /// <returns>A response wrapping <paramref name="value"/> with <paramref name="statusCode"/>.</returns>
  438. public static HttpResponseMessage CreateResponse<T>(this HttpRequestMessage request, HttpStatusCode statusCode, T value, MediaTypeFormatter formatter, string mediaType)
  439. {
  440. MediaTypeHeaderValue mediaTypeHeader = mediaType != null ? new MediaTypeHeaderValue(mediaType) : null;
  441. return request.CreateResponse(statusCode, value, formatter, mediaTypeHeader);
  442. }
  443. /// <summary>
  444. /// Helper method that creates a <see cref="HttpResponseMessage"/> with an <see cref="ObjectContent{T}"/> instance containing the provided
  445. /// <paramref name="value"/> and the given <paramref name="formatter"/>.
  446. /// </summary>
  447. /// <typeparam name="T">The type of the value.</typeparam>
  448. /// <param name="request">The request.</param>
  449. /// <param name="statusCode">The status code of the created response.</param>
  450. /// <param name="value">The value to wrap. Can be <c>null</c>.</param>
  451. /// <param name="formatter">The formatter to use.</param>
  452. /// <param name="mediaType">The media type override to set on the response's content. Can be <c>null</c>.</param>
  453. /// <returns>A response wrapping <paramref name="value"/> with <paramref name="statusCode"/>.</returns>
  454. [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Caller will dispose")]
  455. public static HttpResponseMessage CreateResponse<T>(this HttpRequestMessage request, HttpStatusCode statusCode, T value, MediaTypeFormatter formatter, MediaTypeHeaderValue mediaType)
  456. {
  457. if (request == null)
  458. {
  459. throw Error.ArgumentNull("request");
  460. }
  461. if (formatter == null)
  462. {
  463. throw Error.ArgumentNull("formatter");
  464. }
  465. HttpResponseMessage response = request.CreateResponse(statusCode);
  466. response.Content = new ObjectContent<T>(value, formatter, mediaType);
  467. return response;
  468. }
  469. /// <summary>
  470. /// Adds the given <paramref name="resource"/> to a list of resources that will be disposed by a host once
  471. /// the <paramref name="request"/> is disposed.
  472. /// </summary>
  473. /// <param name="request">The request controlling the lifecycle of <paramref name="resource"/>.</param>
  474. /// <param name="resource">The resource to dispose when <paramref name="request"/> is being disposed. Can be <c>null</c>.</param>
  475. public static void RegisterForDispose(this HttpRequestMessage request, IDisposable resource)
  476. {
  477. if (request == null)
  478. {
  479. throw Error.ArgumentNull("request");
  480. }
  481. if (resource == null)
  482. {
  483. return;
  484. }
  485. List<IDisposable> trackedResources;
  486. if (!request.Properties.TryGetValue(HttpPropertyKeys.DisposableRequestResourcesKey, out trackedResources))
  487. {
  488. trackedResources = new List<IDisposable>();
  489. request.Properties[HttpPropertyKeys.DisposableRequestResourcesKey] = trackedResources;
  490. }
  491. trackedResources.Add(resource);
  492. }
  493. /// <summary>
  494. /// Disposes of all tracked resources associated with the <paramref name="request"/> which were added via the
  495. /// <see cref="RegisterForDispose(HttpRequestMessage, IDisposable)"/> method.
  496. /// </summary>
  497. /// <param name="request">The request.</param>
  498. [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "We want to ignore all exceptions.")]
  499. public static void DisposeRequestResources(this HttpRequestMessage request)
  500. {
  501. if (request == null)
  502. {
  503. throw Error.ArgumentNull("request");
  504. }
  505. List<IDisposable> resourcesToDispose;
  506. if (request.Properties.TryGetValue(HttpPropertyKeys.DisposableRequestResourcesKey, out resourcesToDispose))
  507. {
  508. foreach (IDisposable resource in resourcesToDispose)
  509. {
  510. try
  511. {
  512. resource.Dispose();
  513. }
  514. catch
  515. {
  516. // ignore exceptions
  517. }
  518. }
  519. resourcesToDispose.Clear();
  520. }
  521. }
  522. /// <summary>
  523. /// Retrieves the <see cref="Guid"/> which has been assigned as the
  524. /// correlation id associated with the given <paramref name="request"/>.
  525. /// The value will be created and set the first time this method is called.
  526. /// </summary>
  527. /// <param name="request">The <see cref="HttpRequestMessage"/></param>
  528. /// <returns>The <see cref="Guid"/> associated with that request.</returns>
  529. public static Guid GetCorrelationId(this HttpRequestMessage request)
  530. {
  531. if (request == null)
  532. {
  533. throw Error.ArgumentNull("request");
  534. }
  535. Guid correlationId;
  536. if (!request.Properties.TryGetValue<Guid>(HttpPropertyKeys.RequestCorrelationKey, out correlationId))
  537. {
  538. correlationId = Guid.NewGuid();
  539. request.Properties.Add(HttpPropertyKeys.RequestCorrelationKey, correlationId);
  540. }
  541. return correlationId;
  542. }
  543. /// <summary>
  544. /// Retrieves the parsed query string as a collection of key-value pairs.
  545. /// </summary>
  546. /// <param name="request">The <see cref="HttpRequestMessage"/></param>
  547. /// <returns>The query string as a collection of key-value pairs.</returns>
  548. [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "NameValuePairsValueProvider takes an IEnumerable<KeyValuePair<string, string>>")]
  549. public static IEnumerable<KeyValuePair<string, string>> GetQueryNameValuePairs(this HttpRequestMessage request)
  550. {
  551. if (request == null)
  552. {
  553. throw Error.ArgumentNull("request");
  554. }
  555. Uri uri = request.RequestUri;
  556. // Unit tests may not always provide a Uri in the request
  557. if (uri == null || String.IsNullOrEmpty(uri.Query))
  558. {
  559. return Enumerable.Empty<KeyValuePair<string, string>>();
  560. }
  561. IEnumerable<KeyValuePair<string, string>> queryString;
  562. if (!request.Properties.TryGetValue<IEnumerable<KeyValuePair<string, string>>>(HttpPropertyKeys.RequestQueryNameValuePairsKey, out queryString))
  563. {
  564. // Uri --> FormData --> NVC
  565. FormDataCollection formData = new FormDataCollection(uri);
  566. queryString = formData.GetJQueryNameValuePairs();
  567. request.Properties.Add(HttpPropertyKeys.RequestQueryNameValuePairsKey, queryString);
  568. }
  569. return queryString;
  570. }
  571. public static UrlHelper GetUrlHelper(this HttpRequestMessage request)
  572. {
  573. if (request == null)
  574. {
  575. throw Error.ArgumentNull("request");
  576. }
  577. return new UrlHelper(request);
  578. }
  579. }
  580. }