// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Net; using System.Net.Http; using System.Security.Principal; using System.Text; using Microsoft.AspNet.Http; using Microsoft.AspNet.Mvc; using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.AspNet.Mvc.WebApiCompatShim; using Microsoft.Framework.DependencyInjection; using Newtonsoft.Json; namespace System.Web.Http { [UseWebApiRoutes] [UseWebApiActionConventions] [UseWebApiParameterConventions] [UseWebApiOverloading] public abstract class ApiController : IDisposable { private HttpRequestMessage _request; /// /// Gets the action context. /// /// The setter is intended for unit testing purposes only. [Activate] public ActionContext ActionContext { get; set; } /// /// Gets the . /// /// The setter is intended for unit testing purposes only. [Activate] public ActionBindingContext BindingContext { get; set; } /// /// Gets the http context. /// public HttpContext Context { get { return ActionContext?.HttpContext; } } /// /// Gets the . /// /// The setter is intended for unit testing purposes only. [Activate] public IModelMetadataProvider MetadataProvider { get; set; } /// /// Gets model state after the model binding process. This ModelState will be empty before model binding /// happens. /// 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 { if (_request == null && ActionContext != null) { _request = ActionContext.HttpContext.GetHttpRequestMessage(); } return _request; } set { _request = value; } } /// /// Gets a factory used to generate URLs to other APIs. /// /// The setter is intended for unit testing purposes only. [Activate] public IUrlHelper Url { get; set; } /// /// Gets or sets the current principal associated with this request. /// public IPrincipal User { get { return Context?.User; } } /// /// Creates a (400 Bad Request). /// /// A . [NonAction] public virtual BadRequestResult BadRequest() { return new BadRequestResult(); } /// /// Creates a (400 Bad Request) with the specified error message. /// /// The user-visible error message. /// A with the specified error message. [NonAction] public virtual BadRequestErrorMessageResult BadRequest([NotNull] string message) { return new BadRequestErrorMessageResult(message); } /// /// Creates an (400 Bad Request) with the specified model state. /// /// The model state to include in the error. /// An with the specified model state. [NonAction] public virtual InvalidModelStateResult BadRequest([NotNull] ModelStateDictionary modelState) { return new InvalidModelStateResult(modelState, includeErrorDetail: false); } /// Creates a (409 Conflict). /// A . [NonAction] public virtual ConflictResult Conflict() { return new ConflictResult(); } /// /// 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. [NonAction] public virtual NegotiatedContentResult Content(HttpStatusCode statusCode, [NotNull] T value) { return new NegotiatedContentResult(statusCode, value); } /// /// Creates a (201 Created) with the specified values. /// /// /// The location at which the content has been created. Must be a relative or absolute URL. /// /// The content value to format in the entity body. /// A with the specified values. [NonAction] public virtual CreatedResult Created([NotNull] string location, object content) { return new CreatedResult(location, content); } /// /// Creates a (201 Created) with the specified values. /// /// The location at which the content has been created. /// The content value to format in the entity body. /// A with the specified values. [NonAction] public virtual CreatedResult Created([NotNull] Uri uri, object content) { string location; if (uri.IsAbsoluteUri) { location = uri.AbsoluteUri; } else { location = uri.GetComponents(UriComponents.SerializationInfoString, UriFormat.UriEscaped); } return Created(location, content); } /// /// Creates a (201 Created) with the specified values. /// /// The name of the route to use for generating the URL. /// The route data to use for generating the URL. /// The content value to format in the entity body. /// A with the specified values. [NonAction] public virtual CreatedAtRouteResult CreatedAtRoute( [NotNull] string routeName, object routeValues, object content) { return new CreatedAtRouteResult(routeName, routeValues, content); } /// Creates an (500 Internal Server Error). /// /// A . [NonAction] public virtual InternalServerErrorResult InternalServerError() { return new InternalServerErrorResult(); } /// /// Creates an (500 Internal Server Error) with the specified exception. /// /// The exception to include in the error. /// An with the specified exception. [NonAction] public virtual ExceptionResult InternalServerError([NotNull] Exception exception) { return new ExceptionResult(exception, includeErrorDetail: false); } /// /// Creates an (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. [NonAction] public virtual JsonResult Json([NotNull] T content) { return new JsonResult(content); } /// /// Creates an (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. [NonAction] public virtual JsonResult Json([NotNull] T content, [NotNull] JsonSerializerSettings serializerSettings) { var formatter = new JsonOutputFormatter() { SerializerSettings = serializerSettings, }; return new JsonResult(content, formatter); } /// /// Creates an (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. [NonAction] public virtual JsonResult Json( [NotNull] T content, [NotNull] JsonSerializerSettings serializerSettings, [NotNull] Encoding encoding) { var formatter = new JsonOutputFormatter() { SerializerSettings = serializerSettings, }; formatter.SupportedEncodings.Clear(); formatter.SupportedEncodings.Add(encoding); return new JsonResult(content, formatter); } /// /// Creates an (404 Not Found). /// /// A . [NonAction] public virtual HttpNotFoundResult NotFound() { return new HttpNotFoundResult(); } /// /// Creates an (200 OK). /// /// An . [NonAction] public virtual OkResult Ok() { return new OkResult(); } /// /// 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. [NonAction] public virtual OkNegotiatedContentResult Ok([NotNull] T content) { return new OkNegotiatedContentResult(content); } /// /// Creates a (302 Found) with the specified value. /// /// The location to which to redirect. /// A with the specified value. [NonAction] public virtual RedirectResult Redirect([NotNull] string location) { // This is how redirect was implemented in legacy webapi - string URIs are assumed to be absolute. return Redirect(new Uri(location)); } /// /// Creates a (302 Found) with the specified value. /// /// The location to which to redirect. /// A with the specified value. [NonAction] public virtual RedirectResult Redirect([NotNull] Uri location) { string uri; if (location.IsAbsoluteUri) { uri = location.AbsoluteUri; } else { uri = location.GetComponents(UriComponents.SerializationInfoString, UriFormat.UriEscaped); } return new RedirectResult(uri); } /// /// 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. [NonAction] public virtual RedirectToRouteResult RedirectToRoute([NotNull] string routeName, [NotNull] object routeValues) { return new RedirectToRouteResult(Url, routeName, routeValues); } /// /// Creates a with the specified response. /// /// The HTTP response message. /// A for the specified response. [NonAction] public virtual ResponseMessageResult ResponseMessage([NotNull] 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. [NonAction] public virtual HttpStatusCodeResult StatusCode(HttpStatusCode status) { return new HttpStatusCodeResult((int)status); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// /// Validates the given entity and adds the validation errors to the /// under an empty prefix. /// /// The type of the entity to be validated. /// The entity being validated. public void Validate(TEntity entity) { Validate(entity, keyPrefix: string.Empty); } /// /// Validates the given entity and adds the validation errors to the . /// /// 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 entity, string keyPrefix) { var modelMetadata = MetadataProvider.GetMetadataForType(() => entity, typeof(TEntity)); var bodyValidationExcludeFiltersProvider = Context.RequestServices .GetRequiredService(); var validator = Context.RequestServices.GetRequiredService(); var modelValidationContext = new ModelValidationContext( MetadataProvider, BindingContext.ValidatorProvider, ModelState, modelMetadata, containerMetadata: null, excludeFromValidationFilters: bodyValidationExcludeFiltersProvider.ExcludeFilters); validator.Validate(modelValidationContext, keyPrefix); } protected virtual void Dispose(bool disposing) { } } }