ApiController源码

// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Net;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;
using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http.Controllers;
using System.Web.Http.Dispatcher;
using System.Web.Http.ExceptionHandling;
using System.Web.Http.Filters;
using System.Web.Http.Metadata;
using System.Web.Http.ModelBinding;
using System.Web.Http.Properties;
using System.Web.Http.Results;
using System.Web.Http.Routing;
using System.Web.Http.Validation;
using Newtonsoft.Json;

namespace System.Web.Http
{
    public abstract class ApiController : IHttpController, IDisposable
    {
        private HttpActionContext _actionContext = new HttpActionContext();
        private bool _initialized;

        /// Gets the configuration.
        /// The setter is intended for unit testing purposes only.
        public HttpConfiguration Configuration
        {
            get { return ControllerContext.Configuration; }
            set { ControllerContext.Configuration = value; }
        }

        /// Gets the controller context.
        /// The setter is intended for unit testing purposes only.
        public HttpControllerContext ControllerContext
        {
            get
            {
                // unit test only
                if (ActionContext.ControllerContext == null)
                {
                    ActionContext.ControllerContext = new HttpControllerContext
                    {
                        RequestContext = new RequestBackedHttpRequestContext()
                    };
                }
                return ActionContext.ControllerContext;
            }
            set
            {
                if (value == null)
                {
                    throw Error.PropertyNull();
                }

                ActionContext.ControllerContext = value;
            }
        }

        /// Gets the action context.
        /// The setter is intended for unit testing purposes only.
        public HttpActionContext ActionContext
        {
            get { return _actionContext; }
            set
            {
                if (value == null)
                {
                    throw Error.PropertyNull();
                }
                _actionContext = value;
            }
        }

        /// 
        /// Gets model state after the model binding process. This ModelState will be empty before model binding happens.
        /// 
        /// The setter is intended for unit testing purposes only.
        public ModelStateDictionary ModelState
        {
            get
            {
                return ActionContext.ModelState;
            }
        }

        /// Gets or sets the HTTP request message.
        /// The setter is intended for unit testing purposes only.
        public HttpRequestMessage Request
        {
            get
            {
                return ControllerContext.Request;
            }
            set
            {
                if (value == null)
                {
                    throw Error.PropertyNull();
                }

                HttpRequestContext contextOnRequest = value.GetRequestContext();
                HttpRequestContext contextOnController = RequestContext;

                if (contextOnRequest != null && contextOnRequest != contextOnController)
                {
                    // Prevent unit testers from setting conflicting requests contexts.
                    throw new InvalidOperationException(SRResources.RequestContextConflict);
                }

                ControllerContext.Request = value;
                value.SetRequestContext(contextOnController);

                RequestBackedHttpRequestContext requestBackedContext =
                    contextOnController as RequestBackedHttpRequestContext;

                if (requestBackedContext != null)
                {
                    requestBackedContext.Request = value;
                }
            }
        }

        /// Gets the request context.
        /// The setter is intended for unit testing purposes only.
        public HttpRequestContext RequestContext
        {
            get
            {
                return ControllerContext.RequestContext;
            }
            set
            {
                if (value == null)
                {
                    throw Error.PropertyNull();
                }

                HttpRequestContext oldContext = ControllerContext.RequestContext;
                HttpRequestMessage request = Request;

                if (request != null)
                {
                    HttpRequestContext contextOnRequest = request.GetRequestContext();

                    if (contextOnRequest != null && contextOnRequest != oldContext && contextOnRequest != value)
                    {
                        // Prevent unit testers from setting conflicting requests contexts.
                        throw new InvalidOperationException(SRResources.RequestContextConflict);
                    }

                    request.SetRequestContext(value);
                }

                ControllerContext.RequestContext = value;
            }
        }

        /// Gets a factory used to generate URLs to other APIs.
        /// The setter is intended for unit testing purposes only.
        public UrlHelper Url
        {
            get { return RequestContext.Url; }
            set { RequestContext.Url = value; }
        }

        /// Gets or sets the current principal associated with this request.
        /// The setter is intended for unit testing purposes only.
        public IPrincipal User
        {
            get { return RequestContext.Principal; }
            set { RequestContext.Principal = value; }
        }

        [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "This method is a coordinator, so this coupling is expected.")]
        public virtual Task<HttpResponseMessage> ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken)
        {
            if (_initialized)
            {
                // if user has registered a controller factory which produces the same controller instance, we should throw here
                throw Error.InvalidOperation(SRResources.CannotSupportSingletonInstance, typeof(ApiController).Name, typeof(IHttpControllerActivator).Name);
            }

            Initialize(controllerContext);

            // We can't be reused, and we know we're disposable, so make sure we go away when
            // the request has been completed.
            if (Request != null)
            {
                Request.RegisterForDispose(this);
            }

            HttpControllerDescriptor controllerDescriptor = controllerContext.ControllerDescriptor;
            ServicesContainer controllerServices = controllerDescriptor.Configuration.Services;

            HttpActionDescriptor actionDescriptor = controllerServices.GetActionSelector().SelectAction(controllerContext);
            ActionContext.ActionDescriptor = actionDescriptor;
            if (Request != null)
            {
                Request.SetActionDescriptor(actionDescriptor);
            }

            FilterGrouping filterGrouping = actionDescriptor.GetFilterGrouping();

            IActionFilter[] actionFilters = filterGrouping.ActionFilters;
            IAuthenticationFilter[] authenticationFilters = filterGrouping.AuthenticationFilters;
            IAuthorizationFilter[] authorizationFilters = filterGrouping.AuthorizationFilters;
            IExceptionFilter[] exceptionFilters = filterGrouping.ExceptionFilters;

            IHttpActionResult result = new ActionFilterResult(actionDescriptor.ActionBinding, ActionContext,
                controllerServices, actionFilters);
            if (authorizationFilters.Length > 0)
            {
                result = new AuthorizationFilterResult(ActionContext, authorizationFilters, result);
            }
            if (authenticationFilters.Length > 0)
            {
                result = new AuthenticationFilterResult(ActionContext, this, authenticationFilters, result);
            }
            if (exceptionFilters.Length > 0)
            {
                IExceptionLogger exceptionLogger = ExceptionServices.GetLogger(controllerServices);
                IExceptionHandler exceptionHandler = ExceptionServices.GetHandler(controllerServices);
                result = new ExceptionFilterResult(ActionContext, exceptionFilters, exceptionLogger, exceptionHandler,
                    result);
            }

            return result.ExecuteAsync(cancellationToken);
        }

        /// 
        /// Validates the given entity and adds the validation errors to the 
        /// under the empty prefix, if any.
        /// 
        /// The type of the entity to be validated.
        /// The entity being validated.
        public void Validate<TEntity>(TEntity entity)
        {
            Validate(entity, keyPrefix: String.Empty);
        }

        /// 
        /// Validates the given entity and adds the validation errors to the , if any.
        /// 
        /// The type of the entity to be validated.
        /// The entity being validated.
        /// 
        /// The key prefix under which the model state errors would be added in the .
        /// 
        public void Validate<TEntity>(TEntity entity, string keyPrefix)
        {
            if (Configuration == null)
            {
                throw Error.InvalidOperation(SRResources.TypePropertyMustNotBeNull, typeof(ApiController).Name, "Configuration");
            }

            IBodyModelValidator validator = Configuration.Services.GetBodyModelValidator();
            if (validator != null)
            {
                ModelMetadataProvider metadataProvider = Configuration.Services.GetModelMetadataProvider();
                Contract.Assert(metadataProvider != null, "GetModelMetadataProvider throws on null.");

                validator.Validate(entity, typeof(TEntity), metadataProvider, ActionContext, keyPrefix);
            }
        }

        /// Creates a  (400 Bad Request).
        /// A .
        protected internal virtual BadRequestResult BadRequest()
        {
            return new BadRequestResult(this);
        }

        /// 
        /// Creates a  (400 Bad Request) with the specified error message.
        /// 
        /// The user-visible error message.
        /// A  with the specified error message.
        protected internal virtual BadRequestErrorMessageResult BadRequest(string message)
        {
            return new BadRequestErrorMessageResult(message, this);
        }

        /// 
        /// Creates an  (400 Bad Request) with the specified model state.
        /// 
        /// The model state to include in the error.
        /// An  with the specified model state.
        protected internal virtual InvalidModelStateResult BadRequest(ModelStateDictionary modelState)
        {
            return new InvalidModelStateResult(modelState, this);
        }

        /// Creates a  (409 Conflict).
        /// A .
        protected internal virtual ConflictResult Conflict()
        {
            return new ConflictResult(this);
        }

        /// Creates a  with the specified values.
        /// The type of content in the entity body.
        /// The HTTP status code for the response message.
        /// The content value to negotiate and format in the entity body.
        /// A  with the specified values.
        protected internal virtual NegotiatedContentResult<T> Content<T>(HttpStatusCode statusCode, T value)
        {
            return new NegotiatedContentResult<T>(statusCode, value, this);
        }

        /// Creates a  with the specified values.
        /// The type of content in the entity body.
        /// The HTTP status code for the response message.
        /// The content value to format in the entity body.
        /// The formatter to use to format the content.
        /// A  with the specified values.
        protected internal FormattedContentResult<T> Content<T>(HttpStatusCode statusCode, T value,
            MediaTypeFormatter formatter)
        {
            return Content(statusCode, value, formatter, (MediaTypeHeaderValue)null);
        }

        /// Creates a  with the specified values.
        /// The type of content in the entity body.
        /// The HTTP status code for the response message.
        /// The content value to format in the entity body.
        /// The formatter to use to format the content.
        /// The value for the Content-Type header.
        /// A  with the specified values.
        protected internal FormattedContentResult<T> Content<T>(HttpStatusCode statusCode, T value,
            MediaTypeFormatter formatter, string mediaType)
        {
            return Content(statusCode, value, formatter, new MediaTypeHeaderValue(mediaType));
        }

        /// Creates a  with the specified values.
        /// The type of content in the entity body.
        /// The HTTP status code for the response message.
        /// The content value to format in the entity body.
        /// The formatter to use to format the content.
        /// 
        /// The value for the Content-Type header, or  to have the formatter pick a default
        /// value.
        /// 
        /// A  with the specified values.
        protected internal virtual FormattedContentResult<T> Content<T>(HttpStatusCode statusCode, T value,
            MediaTypeFormatter formatter, MediaTypeHeaderValue mediaType)
        {
            return new FormattedContentResult<T>(statusCode, value, formatter, mediaType, this);
        }

        /// 
        /// Creates a  (201 Created) with the specified values.
        /// 
        /// The type of content in the entity body.
        /// 
        /// The location at which the content has been created. Must be a relative or absolute URL.
        /// 
        /// The content value to negotiate and format in the entity body.
        /// A  with the specified values.
        protected internal CreatedNegotiatedContentResult<T> Created<T>(string location, T content)
        {
            if (location == null)
            {
                throw new ArgumentNullException("location");
            }

            return Created<T>(new Uri(location, UriKind.RelativeOrAbsolute), content);
        }

        /// 
        /// Creates a  (201 Created) with the specified values.
        /// 
        /// The type of content in the entity body.
        /// The location at which the content has been created.
        /// The content value to negotiate and format in the entity body.
        /// A  with the specified values.
        protected internal virtual CreatedNegotiatedContentResult<T> Created<T>(Uri location, T content)
        {
            return new CreatedNegotiatedContentResult<T>(location, content, this);
        }

        /// 
        /// Creates a  (201 Created) with the specified values.
        /// 
        /// The type of content in the entity body.
        /// The name of the route to use for generating the URL.
        /// The route data to use for generating the URL.
        /// The content value to negotiate and format in the entity body.
        /// A  with the specified values.
        protected internal CreatedAtRouteNegotiatedContentResult<T> CreatedAtRoute<T>(string routeName,
            object routeValues, T content)
        {
            return CreatedAtRoute<T>(routeName, new HttpRouteValueDictionary(routeValues), content);
        }

        /// 
        /// Creates a  (201 Created) with the specified values.
        /// 
        /// The type of content in the entity body.
        /// The name of the route to use for generating the URL.
        /// The route data to use for generating the URL.
        /// The content value to negotiate and format in the entity body.
        /// A  with the specified values.
        protected internal virtual CreatedAtRouteNegotiatedContentResult<T> CreatedAtRoute<T>(string routeName,
            IDictionary<string, object> routeValues, T content)
        {
            return new CreatedAtRouteNegotiatedContentResult<T>(routeName, routeValues, content, this);
        }

        /// Creates an  (500 Internal Server Error).
        /// A .
        protected internal virtual InternalServerErrorResult InternalServerError()
        {
            return new InternalServerErrorResult(this);
        }

        /// 
        /// Creates an  (500 Internal Server Error) with the specified exception.
        /// 
        /// The exception to include in the error.
        /// An  with the specified exception.
        protected internal virtual ExceptionResult InternalServerError(Exception exception)
        {
            return new ExceptionResult(exception, this);
        }

        /// Creates a  (200 OK) with the specified value.
        /// The type of content in the entity body.
        /// The content value to serialize in the entity body.
        /// A  with the specified value.
        protected internal JsonResult<T> Json<T>(T content)
        {
            return Json<T>(content, new JsonSerializerSettings());
        }

        /// Creates a  (200 OK) with the specified values.
        /// The type of content in the entity body.
        /// The content value to serialize in the entity body.
        /// The serializer settings.
        /// A  with the specified values.
        protected internal JsonResult<T> Json<T>(T content, JsonSerializerSettings serializerSettings)
        {
            return Json<T>(content, serializerSettings, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false,
                throwOnInvalidBytes: true));
        }

        /// Creates a  (200 OK) with the specified values.
        /// The type of content in the entity body.
        /// The content value to serialize in the entity body.
        /// The serializer settings.
        /// The content encoding.
        /// A  with the specified values.
        protected internal virtual JsonResult<T> Json<T>(T content, JsonSerializerSettings serializerSettings,
            Encoding encoding)
        {
            return new JsonResult<T>(content, serializerSettings, encoding, this);
        }

        /// Creates a  (404 Not Found).
        /// A .
        protected internal virtual NotFoundResult NotFound()
        {
            return new NotFoundResult(this);
        }

        /// Creates an  (200 OK).
        /// An .
        protected internal virtual OkResult Ok()
        {
            return new OkResult(this);
        }

        /// 
        /// Creates an  (200 OK) with the specified values.
        /// 
        /// The type of content in the entity body.
        /// The content value to negotiate and format in the entity body.
        /// An  with the specified values.
        protected internal virtual OkNegotiatedContentResult<T> Ok<T>(T content)
        {
            return new OkNegotiatedContentResult<T>(content, this);
        }

        /// Creates a  (302 Found) with the specified value.
        /// The location to which to redirect.
        /// A  with the specified value.
        protected internal virtual RedirectResult Redirect(string location)
        {
            if (location == null)
            {
                throw new ArgumentNullException("location");
            }

            return Redirect(new Uri(location));
        }

        /// Creates a  (302 Found) with the specified value.
        /// The location to which to redirect.
        /// A  with the specified value.
        protected internal virtual RedirectResult Redirect(Uri location)
        {
            return new RedirectResult(location, this);
        }

        /// Creates a  (302 Found) with the specified values.
        /// The name of the route to use for generating the URL.
        /// The route data to use for generating the URL.
        /// A  with the specified values.
        protected internal RedirectToRouteResult RedirectToRoute(string routeName, object routeValues)
        {
            return RedirectToRoute(routeName, new HttpRouteValueDictionary(routeValues));
        }

        /// Creates a  (302 Found) with the specified values.
        /// The name of the route to use for generating the URL.
        /// The route data to use for generating the URL.
        /// A  with the specified values.
        protected internal virtual RedirectToRouteResult RedirectToRoute(string routeName,
            IDictionary<string, object> routeValues)
        {
            return new RedirectToRouteResult(routeName, routeValues, this);
        }

        /// Creates a  with the specified response.
        /// The HTTP response message.
        /// A  for the specified response.
        protected internal virtual ResponseMessageResult ResponseMessage(HttpResponseMessage response)
        {
            return new ResponseMessageResult(response);
        }

        /// Creates a  with the specified status code.
        /// The HTTP status code for the response message
        /// A  with the specified status code.
        protected internal virtual StatusCodeResult StatusCode(HttpStatusCode status)
        {
            return new StatusCodeResult(status, this);
        }

        /// 
        /// Creates an  (401 Unauthorized) with the specified values.
        /// 
        /// The WWW-Authenticate challenges.
        /// An  with the specified values.
        protected internal UnauthorizedResult Unauthorized(params AuthenticationHeaderValue[] challenges)
        {
            return Unauthorized((IEnumerable<AuthenticationHeaderValue>)challenges);
        }

        /// 
        /// Creates an  (401 Unauthorized) with the specified values.
        /// 
        /// The WWW-Authenticate challenges.
        /// An  with the specified values.
        protected internal virtual UnauthorizedResult Unauthorized(IEnumerable<AuthenticationHeaderValue> challenges)
        {
            return new UnauthorizedResult(challenges, this);
        }

        protected virtual void Initialize(HttpControllerContext controllerContext)
        {
            if (controllerContext == null)
            {
                throw Error.ArgumentNull("controllerContext");
            }

            _initialized = true;
            ControllerContext = controllerContext;
        }

        #region IDisposable

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
        }

        #endregion IDisposable
    }
}

你可能感兴趣的:(ASP.NET,Web,API)