// 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.Net;
using System.Net.Http;
using System.Security.Principal;
using System.Text;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.AspNetCore.Mvc.WebApiCompatShim;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
namespace System.Web.Http
{
[UseWebApiRoutes]
[UseWebApiActionConventions]
[UseWebApiParameterConventions]
[UseWebApiOverloading]
[Controller]
public abstract class ApiController : IDisposable
{
private ControllerContext _controllerContext;
private HttpRequestMessage _request;
private IModelMetadataProvider _metadataProvider;
private IObjectModelValidator _objectValidator;
private IUrlHelper _urlHelper;
///
/// Gets the .
///
public ActionContext ActionContext => ControllerContext;
///
/// Gets or sets the .
///
/// The setter is intended for unit testing purposes only.
[ControllerContext]
public ControllerContext ControllerContext
{
get
{
if (_controllerContext == null)
{
_controllerContext = new ControllerContext();
}
return _controllerContext;
}
set
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
_controllerContext = value;
}
}
///
/// Gets the http context.
///
public HttpContext Context
{
get
{
return ControllerContext.HttpContext;
}
}
///
/// Gets the .
///
/// The setter is intended for unit testing purposes only.
public IModelMetadataProvider MetadataProvider
{
get
{
if (_metadataProvider == null)
{
_metadataProvider = Context?.RequestServices.GetRequiredService();
}
return _metadataProvider;
}
set
{
_metadataProvider = value;
}
}
///
/// Gets or sets the .
///
public IObjectModelValidator ObjectValidator
{
get
{
if (_objectValidator == null)
{
_objectValidator = Context?.RequestServices.GetRequiredService();
}
return _objectValidator;
}
set
{
_objectValidator = value;
}
}
///
/// Gets model state after the model binding process. This ModelState will be empty before model binding
/// happens.
///
public ModelStateDictionary ModelState
{
get
{
return ControllerContext.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 = ControllerContext.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.
public IUrlHelper Url
{
get
{
if (_urlHelper == null)
{
var factory = Context?.RequestServices.GetRequiredService();
_urlHelper = factory?.GetUrlHelper(ActionContext);
}
return _urlHelper;
}
set
{
_urlHelper = value;
}
}
///
/// 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(string message)
{
if (message == null)
{
throw new ArgumentNullException(nameof(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(ModelStateDictionary modelState)
{
if (modelState == null)
{
throw new ArgumentNullException(nameof(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, T value)
{
if (value == null)
{
throw new ArgumentNullException(nameof(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(string location, object content)
{
if (location == null)
{
throw new ArgumentNullException(nameof(location));
}
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(Uri uri, object content)
{
if (uri == null)
{
throw new ArgumentNullException(nameof(uri));
}
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(
string routeName,
object routeValues,
object content)
{
if (routeName == null)
{
throw new ArgumentNullException(nameof(routeName));
}
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(Exception exception)
{
if (exception == null)
{
throw new ArgumentNullException(nameof(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(T content)
{
if (content == null)
{
throw new ArgumentNullException(nameof(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(T content, JsonSerializerSettings serializerSettings)
{
if (content == null)
{
throw new ArgumentNullException(nameof(content));
}
if (serializerSettings == null)
{
throw new ArgumentNullException(nameof(serializerSettings));
}
return new JsonResult(content, serializerSettings);
}
///
/// 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(
T content,
JsonSerializerSettings serializerSettings,
Encoding encoding)
{
if (content == null)
{
throw new ArgumentNullException(nameof(content));
}
if (serializerSettings == null)
{
throw new ArgumentNullException(nameof(serializerSettings));
}
if (encoding == null)
{
throw new ArgumentNullException(nameof(encoding));
}
var result = new JsonResult(content, serializerSettings);
result.ContentType = $"application/json; charset={encoding.WebName}";
return result;
}
///
/// Creates an (404 Not Found).
///
/// A .
[NonAction]
public virtual NotFoundResult NotFound()
{
return new NotFoundResult();
}
///
/// 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 OkObjectResult Ok(T content)
{
return new OkObjectResult(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(string location)
{
if (location == null)
{
throw new ArgumentNullException(nameof(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(Uri location)
{
if (location == null)
{
throw new ArgumentNullException(nameof(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(string routeName, object routeValues)
{
if (routeName == null)
{
throw new ArgumentNullException(nameof(routeName));
}
if (routeValues == null)
{
throw new ArgumentNullException(nameof(routeValues));
}
return new RedirectToRouteResult(routeName, routeValues)
{
UrlHelper = Url,
};
}
///
/// Creates a with the specified response.
///
/// The HTTP response message.
/// A for the specified response.
[NonAction]
public virtual ResponseMessageResult ResponseMessage(HttpResponseMessage response)
{
if (response == null)
{
throw new ArgumentNullException(nameof(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 StatusCodeResult StatusCode(HttpStatusCode status)
{
return new StatusCodeResult((int)status);
}
///
public void Dispose() => Dispose(disposing: true);
///
/// 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)
{
ObjectValidator.Validate(
ControllerContext,
validationState: null,
prefix: keyPrefix,
model: entity);
}
protected virtual void Dispose(bool disposing)
{
}
}
}