// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Net.Http.Formatting; using System.Net.Http.Headers; using System.Threading; using System.Web.Http; using System.Web.Http.Dependencies; using System.Web.Http.Hosting; using System.Web.Http.Properties; using System.Web.Http.Routing; namespace System.Net.Http { /// /// Provides extension methods for the class. /// [EditorBrowsable(EditorBrowsableState.Never)] public static class HttpRequestMessageExtensions { /// /// Gets the for the given request. /// /// The HTTP request. /// The . public static HttpConfiguration GetConfiguration(this HttpRequestMessage request) { if (request == null) { throw Error.ArgumentNull("request"); } return request.GetProperty(HttpPropertyKeys.HttpConfigurationKey); } /// /// Gets the dependency resolver scope associated with this . /// Services which are retrieved from this scope will be released when the request is /// cleaned up by the framework. /// /// The HTTP request. /// The for the given request. public static IDependencyScope GetDependencyScope(this HttpRequestMessage request) { if (request == null) { throw Error.ArgumentNull("request"); } IDependencyScope result; if (!request.Properties.TryGetValue(HttpPropertyKeys.DependencyScope, out result)) { result = request.GetConfiguration().DependencyResolver.BeginScope(); request.Properties[HttpPropertyKeys.DependencyScope] = result; request.RegisterForDispose(result); } return result; } /// /// Gets the for the given request or null if not available. /// /// The HTTP request. /// The or null. public static SynchronizationContext GetSynchronizationContext(this HttpRequestMessage request) { if (request == null) { throw Error.ArgumentNull("request"); } return request.GetProperty(HttpPropertyKeys.SynchronizationContextKey); } /// /// Gets the for the given request or null if not available. /// /// The HTTP request. /// The or null. public static IHttpRouteData GetRouteData(this HttpRequestMessage request) { if (request == null) { throw Error.ArgumentNull("request"); } return request.GetProperty(HttpPropertyKeys.HttpRouteDataKey); } private static T GetProperty(this HttpRequestMessage request, string key) { T value; request.Properties.TryGetValue(key, out value); return value; } /// /// Helper method that performs content negotiation and creates a with an instance /// of as the content if a formatter can be found. If no formatter is found that this /// method returns a response with status 406 NotAcceptable. This forwards the call to /// with a null /// configuration. /// /// /// This method requires that has been associated with an instance of /// . /// /// The type of the value. /// The request. /// The status code of the created response. /// The value to wrap. Can be null. /// A response wrapping with . public static HttpResponseMessage CreateResponse(this HttpRequestMessage request, HttpStatusCode statusCode, T value) { return request.CreateResponse(statusCode, value, configuration: null); } /// /// Helper method that performs content negotiation and creates a with an instance /// of as the content if a formatter can be found. If no formatter is found that this /// method returns a response with status 406 NotAcceptable. /// /// /// This method will use the provided or it will get the /// instance associated with . /// /// The type of the value. /// The request. /// The status code of the created response. /// The value to wrap. Can be null. /// The configuration to use. Can be null. /// A response wrapping with . [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Caller will dispose")] public static HttpResponseMessage CreateResponse(this HttpRequestMessage request, HttpStatusCode statusCode, T value, HttpConfiguration configuration) { if (request == null) { throw Error.ArgumentNull("request"); } configuration = configuration ?? request.GetConfiguration(); if (configuration == null) { throw Error.InvalidOperation(SRResources.HttpRequestMessageExtensions_NoConfiguration); } IContentNegotiator contentNegotiator = configuration.Services.GetContentNegotiator(); if (contentNegotiator == null) { throw Error.InvalidOperation(SRResources.HttpRequestMessageExtensions_NoContentNegotiator, typeof(IContentNegotiator).FullName); } IEnumerable formatters = configuration.Formatters; // Run content negotiation ContentNegotiationResult result = contentNegotiator.Negotiate(typeof(T), request, formatters); if (result == null) { // no result from content negotiation indicates that 406 should be sent. return new HttpResponseMessage { StatusCode = HttpStatusCode.NotAcceptable, RequestMessage = request, }; } else { MediaTypeHeaderValue mediaType = result.MediaType; return new HttpResponseMessage { Content = new ObjectContent(value, result.Formatter, mediaType != null ? mediaType.ToString() : null), StatusCode = statusCode, RequestMessage = request }; } } /// /// Helper method that creates a with an instance containing the provided /// . The given is used to find an instance of . /// /// The type of the value. /// The request. /// The status code of the created response. /// The value to wrap. Can be null. /// The media type used to look up an instance of . /// Thrown if the does not have an associated /// instance or if the configuration does not have a formatter matching . /// A response wrapping with . public static HttpResponseMessage CreateResponse(this HttpRequestMessage request, HttpStatusCode statusCode, T value, string mediaType) { return request.CreateResponse(statusCode, value, new MediaTypeHeaderValue(mediaType)); } /// /// Helper method that creates a with an instance containing the provided /// . The given is used to find an instance of . /// /// The type of the value. /// The request. /// The status code of the created response. /// The value to wrap. Can be null. /// The media type used to look up an instance of . /// Thrown if the does not have an associated /// instance or if the configuration does not have a formatter matching . /// A response wrapping with . public static HttpResponseMessage CreateResponse(this HttpRequestMessage request, HttpStatusCode statusCode, T value, MediaTypeHeaderValue mediaType) { if (request == null) { throw Error.ArgumentNull("request"); } if (mediaType == null) { throw Error.ArgumentNull("mediaType"); } HttpConfiguration configuration = request.GetConfiguration(); if (configuration == null) { throw Error.InvalidOperation(SRResources.HttpRequestMessageExtensions_NoConfiguration); } MediaTypeFormatter formatter = configuration.Formatters.FindWriter(typeof(T), mediaType); if (formatter == null) { throw Error.InvalidOperation(SRResources.HttpRequestMessageExtensions_NoMatchingFormatter, mediaType, typeof(T).Name); } return request.CreateResponse(statusCode, value, formatter, mediaType); } /// /// Helper method that creates a with an instance containing the provided /// and the given . /// /// The type of the value. /// The request. /// The status code of the created response. /// The value to wrap. Can be null. /// The formatter to use. /// A response wrapping with . public static HttpResponseMessage CreateResponse(this HttpRequestMessage request, HttpStatusCode statusCode, T value, MediaTypeFormatter formatter) { return request.CreateResponse(statusCode, value, formatter, (MediaTypeHeaderValue)null); } /// /// Helper method that creates a with an instance containing the provided /// and the given . /// /// The type of the value. /// The request. /// The status code of the created response. /// The value to wrap. Can be null. /// The formatter to use. /// The media type override to set on the response's content. Can be null. /// A response wrapping with . public static HttpResponseMessage CreateResponse(this HttpRequestMessage request, HttpStatusCode statusCode, T value, MediaTypeFormatter formatter, string mediaType) { MediaTypeHeaderValue mediaTypeHeader = mediaType != null ? new MediaTypeHeaderValue(mediaType) : null; return request.CreateResponse(statusCode, value, formatter, mediaTypeHeader); } /// /// Helper method that creates a with an instance containing the provided /// and the given . /// /// The type of the value. /// The request. /// The status code of the created response. /// The value to wrap. Can be null. /// The formatter to use. /// The media type override to set on the response's content. Can be null. /// A response wrapping with . [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Caller will dispose")] public static HttpResponseMessage CreateResponse(this HttpRequestMessage request, HttpStatusCode statusCode, T value, MediaTypeFormatter formatter, MediaTypeHeaderValue mediaType) { if (request == null) { throw Error.ArgumentNull("request"); } if (formatter == null) { throw Error.ArgumentNull("formatter"); } HttpResponseMessage response = request.CreateResponse(statusCode); // TODO pass in full mediaType when OC gets the right ctor overload. string mediaTypeValue = mediaType != null ? mediaType.MediaType : null; response.Content = new ObjectContent(value, formatter, mediaTypeValue); return response; } /// /// Adds the given to a list of resources that will be disposed by a host once /// the is disposed. /// /// The request controlling the lifecycle of . /// The resource to dispose when is being disposed. Can be null. public static void RegisterForDispose(this HttpRequestMessage request, IDisposable resource) { if (request == null) { throw Error.ArgumentNull("request"); } if (resource == null) { return; } List trackedResources; if (!request.Properties.TryGetValue(HttpPropertyKeys.DisposableRequestResourcesKey, out trackedResources)) { trackedResources = new List(); request.Properties[HttpPropertyKeys.DisposableRequestResourcesKey] = trackedResources; } trackedResources.Add(resource); } /// /// Disposes of all tracked resources associated with the which were added via the /// method. /// /// The request. [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "We want to ignore all exceptions.")] public static void DisposeRequestResources(this HttpRequestMessage request) { if (request == null) { throw Error.ArgumentNull("request"); } List resourcesToDispose; if (request.Properties.TryGetValue(HttpPropertyKeys.DisposableRequestResourcesKey, out resourcesToDispose)) { foreach (IDisposable resource in resourcesToDispose) { try { resource.Dispose(); } catch { // ignore exceptions } } resourcesToDispose.Clear(); } } /// /// Retrieves the which has been assigned as the /// correlation id associated with the given . /// The value will be created and set the first time this method is called. /// /// The /// The associated with that request. public static Guid GetCorrelationId(this HttpRequestMessage request) { if (request == null) { throw Error.ArgumentNull("request"); } Guid correlationId; if (!request.Properties.TryGetValue(HttpPropertyKeys.RequestCorrelationKey, out correlationId)) { correlationId = Guid.NewGuid(); request.Properties.Add(HttpPropertyKeys.RequestCorrelationKey, correlationId); } return correlationId; } } }