From a2393f21be55c5c02ea67df55407aa7585121173 Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Thu, 10 Dec 2015 11:19:22 -0800 Subject: [PATCH] Adds ControllerBase to Mvc.Core This change adds a base class for controllers to Mvc.Core that can be used without a dependency on views, json, etc. Most of the functionality on Controller moves to ControllerBase. I kept the IActionFilter and IDisposable functionality on Controller since it's not really a fit with the 'minimal' philosophy. --- .../ControllerBase.cs | 1451 ++++++++++++++ .../Controller.cs | 1441 +------------- .../ControllerBaseTest.cs | 1699 +++++++++++++++++ .../DefaultControllerTypeProviderTest.cs | 53 +- .../ControllerTest.cs | 1635 +--------------- 5 files changed, 3206 insertions(+), 3073 deletions(-) create mode 100644 src/Microsoft.AspNet.Mvc.Core/ControllerBase.cs create mode 100644 test/Microsoft.AspNet.Mvc.Core.Test/ControllerBaseTest.cs diff --git a/src/Microsoft.AspNet.Mvc.Core/ControllerBase.cs b/src/Microsoft.AspNet.Mvc.Core/ControllerBase.cs new file mode 100644 index 0000000000..7e68bae65c --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/ControllerBase.cs @@ -0,0 +1,1451 @@ +// 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; +using System.Collections.Generic; +using System.IO; +using System.Linq.Expressions; +using System.Security.Claims; +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNet.Http; +using Microsoft.AspNet.Http.Authentication; +using Microsoft.AspNet.Mvc.Core; +using Microsoft.AspNet.Mvc.ModelBinding; +using Microsoft.AspNet.Mvc.ModelBinding.Validation; +using Microsoft.AspNet.Mvc.Routing; +using Microsoft.AspNet.Routing; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Net.Http.Headers; + +namespace Microsoft.AspNet.Mvc +{ + /// + /// A base class for an MVC controller without view support. + /// + public abstract class ControllerBase + { + private ControllerContext _controllerContext; + private IModelMetadataProvider _metadataProvider; + private IObjectModelValidator _objectValidator; + private IUrlHelper _url; + + /// + /// Gets the request-specific . + /// + public IServiceProvider Resolver + { + get + { + return HttpContext?.RequestServices; + } + } + + /// + /// Gets the for the executing action. + /// + public HttpContext HttpContext + { + get + { + return ControllerContext.HttpContext; + } + } + + /// + /// Gets the for the executing action. + /// + public HttpRequest Request + { + get + { + return HttpContext?.Request; + } + } + + /// + /// Gets the for the executing action. + /// + public HttpResponse Response + { + get + { + return HttpContext?.Response; + } + } + + /// + /// Gets the for the executing action. + /// + public RouteData RouteData + { + get + { + return ControllerContext.RouteData; + } + } + + /// + /// Gets the that contains the state of the model and of model-binding validation. + /// + public ModelStateDictionary ModelState + { + get + { + return ControllerContext?.ModelState; + } + } + + /// + /// Gets or sets the . + /// + /// + /// activates this property while activating controllers. + /// If user code directly instantiates a controller, the getter returns an empty + /// . + /// + [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 or sets the . + /// + public IModelMetadataProvider MetadataProvider + { + get + { + if (_metadataProvider == null) + { + _metadataProvider = Resolver?.GetRequiredService(); + } + + return _metadataProvider; + } + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + _metadataProvider = value; + } + } + + /// + /// Gets or sets the . + /// + public IUrlHelper Url + { + get + { + if (_url == null) + { + var factory = Resolver?.GetRequiredService(); + _url = factory?.GetUrlHelper(ControllerContext); + } + + return _url; + } + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + _url = value; + } + } + + /// + /// Gets or sets the . + /// + public IObjectModelValidator ObjectValidator + { + get + { + if (_objectValidator == null) + { + _objectValidator = Resolver?.GetRequiredService(); + } + + return _objectValidator; + } + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + _objectValidator = value; + } + } + + /// + /// Gets or sets the for user associated with the executing action. + /// + public ClaimsPrincipal User + { + get + { + return HttpContext?.User; + } + } + + /// + /// Creates a object by specifying a string. + /// + /// The content to write to the response. + /// The created object for the response. + [NonAction] + public virtual ContentResult Content(string content) + { + return Content(content, (MediaTypeHeaderValue)null); + } + + /// + /// Creates a object by specifying a string + /// and a content type. + /// + /// The content to write to the response. + /// The content type (MIME type). + /// The created object for the response. + [NonAction] + public virtual ContentResult Content(string content, string contentType) + { + return Content(content, MediaTypeHeaderValue.Parse(contentType)); + } + + /// + /// Creates a object by specifying a string, + /// a , and . + /// + /// The content to write to the response. + /// The content type (MIME type). + /// The content encoding. + /// The created object for the response. + /// + /// If encoding is provided by both the 'charset' and the parameters, then + /// the parameter is chosen as the final encoding. + /// + [NonAction] + public virtual ContentResult Content(string content, string contentType, Encoding contentEncoding) + { + var mediaTypeHeaderValue = MediaTypeHeaderValue.Parse(contentType); + mediaTypeHeaderValue.Encoding = contentEncoding ?? mediaTypeHeaderValue.Encoding; + return Content(content, mediaTypeHeaderValue); + } + + /// + /// Creates a object by specifying a + /// string and a . + /// + /// The content to write to the response. + /// The content type (MIME type). + /// The created object for the response. + [NonAction] + public virtual ContentResult Content(string content, MediaTypeHeaderValue contentType) + { + var result = new ContentResult + { + Content = content, + ContentType = contentType + }; + + return result; + } + + + /// + /// Creates a object that produces an empty No Content (204) response. + /// + /// The created object for the response. + [NonAction] + public virtual NoContentResult NoContent() + { + return new NoContentResult(); + } + + /// + /// Creates a object that produces an empty OK (200) response. + /// + /// The created for the response. + [NonAction] + public virtual HttpOkResult Ok() + { + return new HttpOkResult(); + } + + /// + /// Creates an object that produces an OK (200) response. + /// + /// The content value to format in the entity body. + /// The created for the response. + [NonAction] + public virtual HttpOkObjectResult Ok(object value) + { + var disposableValue = value as IDisposable; + if (disposableValue != null) + { + Response.RegisterForDispose(disposableValue); + } + + return new HttpOkObjectResult(value); + } + + /// + /// Creates a object that redirects to the specified . + /// + /// The URL to redirect to. + /// The created for the response. + [NonAction] + public virtual RedirectResult Redirect(string url) + { + if (string.IsNullOrEmpty(url)) + { + throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(url)); + } + + return new RedirectResult(url); + } + + /// + /// Creates a object with set to true + /// using the specified . + /// + /// The URL to redirect to. + /// The created for the response. + [NonAction] + public virtual RedirectResult RedirectPermanent(string url) + { + if (string.IsNullOrEmpty(url)) + { + throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(url)); + } + + return new RedirectResult(url, permanent: true); + } + + /// + /// Creates a object that redirects to + /// the specified local . + /// + /// The local URL to redirect to. + /// The created for the response. + [NonAction] + public virtual LocalRedirectResult LocalRedirect(string localUrl) + { + if (string.IsNullOrEmpty(localUrl)) + { + throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(localUrl)); + } + + return new LocalRedirectResult(localUrl); + } + + /// + /// Creates a object with + /// set to true using the specified . + /// + /// The local URL to redirect to. + /// The created for the response. + [NonAction] + public virtual LocalRedirectResult LocalRedirectPermanent(string localUrl) + { + if (string.IsNullOrEmpty(localUrl)) + { + throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(localUrl)); + } + + return new LocalRedirectResult(localUrl, permanent: true); + } + + /// + /// Redirects to the specified action using the . + /// + /// The name of the action. + /// The created for the response. + [NonAction] + public virtual RedirectToActionResult RedirectToAction(string actionName) + { + return RedirectToAction(actionName, routeValues: null); + } + + /// + /// Redirects to the specified action using the + /// and . + /// + /// The name of the action. + /// The parameters for a route. + /// The created for the response. + [NonAction] + public virtual RedirectToActionResult RedirectToAction(string actionName, object routeValues) + { + return RedirectToAction(actionName, controllerName: null, routeValues: routeValues); + } + + /// + /// Redirects to the specified action using the + /// and the . + /// + /// The name of the action. + /// The name of the controller. + /// The created for the response. + [NonAction] + public virtual RedirectToActionResult RedirectToAction(string actionName, string controllerName) + { + return RedirectToAction(actionName, controllerName, routeValues: null); + } + + /// + /// Redirects to the specified action using the specified , + /// , and . + /// + /// The name of the action. + /// The name of the controller. + /// The parameters for a route. + /// The created for the response. + [NonAction] + public virtual RedirectToActionResult RedirectToAction( + string actionName, + string controllerName, + object routeValues) + { + return new RedirectToActionResult(actionName, controllerName, routeValues) + { + UrlHelper = Url, + }; + } + + /// + /// Redirects to the specified action with set to true + /// using the specified . + /// + /// The name of the action. + /// The created for the response. + [NonAction] + public virtual RedirectToActionResult RedirectToActionPermanent(string actionName) + { + return RedirectToActionPermanent(actionName, routeValues: null); + } + + /// + /// Redirects to the specified action with set to true + /// using the specified and . + /// + /// The name of the action. + /// The parameters for a route. + /// The created for the response. + [NonAction] + public virtual RedirectToActionResult RedirectToActionPermanent(string actionName, object routeValues) + { + return RedirectToActionPermanent(actionName, controllerName: null, routeValues: routeValues); + } + + /// + /// Redirects to the specified action with set to true + /// using the specified and . + /// + /// The name of the action. + /// The name of the controller. + /// The created for the response. + [NonAction] + public virtual RedirectToActionResult RedirectToActionPermanent(string actionName, string controllerName) + { + return RedirectToActionPermanent(actionName, controllerName, routeValues: null); + } + + /// + /// Redirects to the specified action with set to true + /// using the specified , , + /// and . + /// + /// The name of the action. + /// The name of the controller. + /// The parameters for a route. + /// The created for the response. + [NonAction] + public virtual RedirectToActionResult RedirectToActionPermanent( + string actionName, + string controllerName, + object routeValues) + { + return new RedirectToActionResult( + actionName, + controllerName, + routeValues, + permanent: true) + { + UrlHelper = Url, + }; + } + + /// + /// Redirects to the specified route using the specified . + /// + /// The name of the route. + /// The created for the response. + [NonAction] + public virtual RedirectToRouteResult RedirectToRoute(string routeName) + { + return RedirectToRoute(routeName, routeValues: null); + } + + /// + /// Redirects to the specified route using the specified . + /// + /// The parameters for a route. + /// The created for the response. + [NonAction] + public virtual RedirectToRouteResult RedirectToRoute(object routeValues) + { + return RedirectToRoute(routeName: null, routeValues: routeValues); + } + + /// + /// Redirects to the specified route using the specified + /// and . + /// + /// The name of the route. + /// The parameters for a route. + /// The created for the response. + [NonAction] + public virtual RedirectToRouteResult RedirectToRoute(string routeName, object routeValues) + { + return new RedirectToRouteResult(routeName, routeValues) + { + UrlHelper = Url, + }; + } + + /// + /// Redirects to the specified route with set to true + /// using the specified . + /// + /// The name of the route. + /// The created for the response. + [NonAction] + public virtual RedirectToRouteResult RedirectToRoutePermanent(string routeName) + { + return RedirectToRoutePermanent(routeName, routeValues: null); + } + + /// + /// Redirects to the specified route with set to true + /// using the specified . + /// + /// The parameters for a route. + /// The created for the response. + [NonAction] + public virtual RedirectToRouteResult RedirectToRoutePermanent(object routeValues) + { + return RedirectToRoutePermanent(routeName: null, routeValues: routeValues); + } + + /// + /// Redirects to the specified route with set to true + /// using the specified and . + /// + /// The name of the route. + /// The parameters for a route. + /// The created for the response. + [NonAction] + public virtual RedirectToRouteResult RedirectToRoutePermanent(string routeName, object routeValues) + { + return new RedirectToRouteResult(routeName, routeValues, permanent: true) + { + UrlHelper = Url, + }; + } + + /// + /// Returns a file with the specified as content and the + /// specified as the Content-Type. + /// + /// The file contents. + /// The Content-Type of the file. + /// The created for the response. + [NonAction] + public virtual FileContentResult File(byte[] fileContents, string contentType) + { + return File(fileContents, contentType, fileDownloadName: null); + } + + /// + /// Returns a file with the specified as content, the + /// specified as the Content-Type and the + /// specified as the suggested file name. + /// + /// The file contents. + /// The Content-Type of the file. + /// The suggested file name. + /// The created for the response. + [NonAction] + public virtual FileContentResult File(byte[] fileContents, string contentType, string fileDownloadName) + { + return new FileContentResult(fileContents, contentType) { FileDownloadName = fileDownloadName }; + } + + /// + /// Returns a file in the specified with the + /// specified as the Content-Type. + /// + /// The with the contents of the file. + /// The Content-Type of the file. + /// The created for the response. + [NonAction] + public virtual FileStreamResult File(Stream fileStream, string contentType) + { + return File(fileStream, contentType, fileDownloadName: null); + } + + /// + /// Returns a file in the specified with the + /// specified as the Content-Type and the + /// specified as the suggested file name. + /// + /// The with the contents of the file. + /// The Content-Type of the file. + /// The suggested file name. + /// The created for the response. + [NonAction] + public virtual FileStreamResult File(Stream fileStream, string contentType, string fileDownloadName) + { + if (fileStream != null) + { + Response.RegisterForDispose(fileStream); + } + + return new FileStreamResult(fileStream, contentType) { FileDownloadName = fileDownloadName }; + } + + /// + /// Returns the file specified by with the + /// specified as the Content-Type. + /// + /// The virtual path of the file to be returned. + /// The Content-Type of the file. + /// The created for the response. + [NonAction] + public virtual VirtualFileResult File(string virtualPath, string contentType) + { + return File(virtualPath, contentType, fileDownloadName: null); + } + + /// + /// Returns the file specified by with the + /// specified as the Content-Type and the + /// specified as the suggested file name. + /// + /// The virtual path of the file to be returned. + /// The Content-Type of the file. + /// The suggested file name. + /// The created for the response. + [NonAction] + public virtual VirtualFileResult File(string virtualPath, string contentType, string fileDownloadName) + { + return new VirtualFileResult(virtualPath, contentType) { FileDownloadName = fileDownloadName }; + } + + /// + /// Returns the file specified by with the + /// specified as the Content-Type. + /// + /// The physical path of the file to be returned. + /// The Content-Type of the file. + /// The created for the response. + [NonAction] + public virtual PhysicalFileResult PhysicalFile(string physicalPath, string contentType) + { + return PhysicalFile(physicalPath, contentType, fileDownloadName: null); + } + + /// + /// Returns the file specified by with the + /// specified as the Content-Type and the + /// specified as the suggested file name. + /// + /// The physical path of the file to be returned. + /// The Content-Type of the file. + /// The suggested file name. + /// The created for the response. + [NonAction] + public virtual PhysicalFileResult PhysicalFile( + string physicalPath, + string contentType, + string fileDownloadName) + { + return new PhysicalFileResult(physicalPath, contentType) { FileDownloadName = fileDownloadName }; + } + + /// + /// Creates an that produces an Unauthorized (401) response. + /// + /// The created for the response. + [NonAction] + public virtual HttpUnauthorizedResult HttpUnauthorized() + { + return new HttpUnauthorizedResult(); + } + + /// + /// Creates an that produces a Not Found (404) response. + /// + /// The created for the response. + [NonAction] + public virtual HttpNotFoundResult HttpNotFound() + { + return new HttpNotFoundResult(); + } + + /// + /// Creates an that produces a Not Found (404) response. + /// + /// The created for the response. + [NonAction] + public virtual HttpNotFoundObjectResult HttpNotFound(object value) + { + var disposableValue = value as IDisposable; + if (disposableValue != null) + { + Response.RegisterForDispose(disposableValue); + } + + return new HttpNotFoundObjectResult(value); + } + + /// + /// Creates an that produces a Bad Request (400) response. + /// + /// The created for the response. + [NonAction] + public virtual BadRequestResult HttpBadRequest() + { + return new BadRequestResult(); + } + + /// + /// Creates an that produces a Bad Request (400) response. + /// + /// The created for the response. + [NonAction] + public virtual BadRequestObjectResult HttpBadRequest(object error) + { + var disposableValue = error as IDisposable; + if (disposableValue != null) + { + Response.RegisterForDispose(disposableValue); + } + + return new BadRequestObjectResult(error); + } + + /// + /// Creates an that produces a Bad Request (400) response. + /// + /// The created for the response. + [NonAction] + public virtual BadRequestObjectResult HttpBadRequest(ModelStateDictionary modelState) + { + if (modelState == null) + { + throw new ArgumentNullException(nameof(modelState)); + } + + return new BadRequestObjectResult(modelState); + } + + /// + /// Creates a object that produces a Created (201) response. + /// + /// The URI at which the content has been created. + /// The content value to format in the entity body. + /// The created for the response. + [NonAction] + public virtual CreatedResult Created(string uri, object value) + { + if (uri == null) + { + throw new ArgumentNullException(nameof(uri)); + } + + var disposableValue = value as IDisposable; + if (disposableValue != null) + { + Response.RegisterForDispose(disposableValue); + } + + return new CreatedResult(uri, value); + } + + /// + /// Creates a object that produces a Created (201) response. + /// + /// The URI at which the content has been created. + /// The content value to format in the entity body. + /// The created for the response. + [NonAction] + public virtual CreatedResult Created(Uri uri, object value) + { + if (uri == null) + { + throw new ArgumentNullException(nameof(uri)); + } + + var disposableValue = value as IDisposable; + if (disposableValue != null) + { + Response.RegisterForDispose(disposableValue); + } + + return new CreatedResult(uri, value); + } + + /// + /// Creates a object that produces a Created (201) response. + /// + /// The name of the action to use for generating the URL. + /// The content value to format in the entity body. + /// The created for the response. + [NonAction] + public virtual CreatedAtActionResult CreatedAtAction(string actionName, object value) + { + return CreatedAtAction(actionName, routeValues: null, value: value); + } + + /// + /// Creates a object that produces a Created (201) response. + /// + /// The name of the action to use for generating the URL. + /// The route data to use for generating the URL. + /// The content value to format in the entity body. + /// The created for the response. + [NonAction] + public virtual CreatedAtActionResult CreatedAtAction(string actionName, object routeValues, object value) + { + return CreatedAtAction(actionName, controllerName: null, routeValues: routeValues, value: value); + } + + /// + /// Creates a object that produces a Created (201) response. + /// + /// The name of the action to use for generating the URL. + /// The name of the controller to use for generating the URL. + /// The route data to use for generating the URL. + /// The content value to format in the entity body. + /// The created for the response. + [NonAction] + public virtual CreatedAtActionResult CreatedAtAction( + string actionName, + string controllerName, + object routeValues, + object value) + { + var disposableValue = value as IDisposable; + if (disposableValue != null) + { + Response.RegisterForDispose(disposableValue); + } + + return new CreatedAtActionResult(actionName, controllerName, routeValues, value); + } + + /// + /// Creates a object that produces a Created (201) response. + /// + /// The name of the route to use for generating the URL. + /// The content value to format in the entity body. + /// The created for the response. + [NonAction] + public virtual CreatedAtRouteResult CreatedAtRoute(string routeName, object value) + { + return CreatedAtRoute(routeName, routeValues: null, value: value); + } + + /// + /// Creates a object that produces a Created (201) response. + /// + /// The route data to use for generating the URL. + /// The content value to format in the entity body. + /// The created for the response. + [NonAction] + public virtual CreatedAtRouteResult CreatedAtRoute(object routeValues, object value) + { + return CreatedAtRoute(routeName: null, routeValues: routeValues, value: value); + } + + /// + /// Creates a object that produces a Created (201) response. + /// + /// 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. + /// The created for the response. + [NonAction] + public virtual CreatedAtRouteResult CreatedAtRoute(string routeName, object routeValues, object value) + { + var disposableValue = value as IDisposable; + if (disposableValue != null) + { + Response.RegisterForDispose(disposableValue); + } + + return new CreatedAtRouteResult(routeName, routeValues, value); + } + + /// + /// Creates a . + /// + /// The created for the response. + [NonAction] + public virtual ChallengeResult Challenge() + => new ChallengeResult(); + + /// + /// Creates a with the specified authentication scheme. + /// + /// The authentication scheme to challenge. + /// The created for the response. + [NonAction] + public virtual ChallengeResult Challenge(string authenticationScheme) + => new ChallengeResult(authenticationScheme); + + /// + /// Creates a with the specified authentication schemes. + /// + /// The authentication schemes to challenge. + /// The created for the response. + [NonAction] + public virtual ChallengeResult Challenge(IList authenticationSchemes) + => new ChallengeResult(authenticationSchemes); + + /// + /// Creates a with the specified . + /// + /// used to perform the authentication + /// challenge. + /// The created for the response. + [NonAction] + public virtual ChallengeResult Challenge(AuthenticationProperties properties) + => new ChallengeResult(properties); + + /// + /// Creates a with the specified specified authentication scheme and + /// . + /// + /// The authentication scheme to challenge. + /// used to perform the authentication + /// challenge. + /// The created for the response. + [NonAction] + public virtual ChallengeResult Challenge(string authenticationScheme, AuthenticationProperties properties) + => new ChallengeResult(authenticationScheme, properties); + + /// + /// Creates a with the specified specified authentication schemes and + /// . + /// + /// The authentication schemes to challenge. + /// used to perform the authentication + /// challenge. + /// The created for the response. + [NonAction] + public virtual ChallengeResult Challenge( + IList authenticationSchemes, + AuthenticationProperties properties) + => new ChallengeResult(authenticationSchemes, properties); + + /// + /// Creates a . + /// + /// The created for the response. + [NonAction] + public virtual ForbidResult Forbid() + => new ForbidResult(); + + /// + /// Creates a with the specified authentication scheme. + /// + /// The authentication scheme to challenge. + /// The created for the response. + [NonAction] + public virtual ForbidResult Forbid(string authenticationScheme) + => new ForbidResult(authenticationScheme); + + /// + /// Creates a with the specified authentication schemes. + /// + /// The authentication schemes to challenge. + /// The created for the response. + [NonAction] + public virtual ForbidResult Forbid(IList authenticationSchemes) + => new ForbidResult(authenticationSchemes); + + /// + /// Creates a with the specified . + /// + /// used to perform the authentication + /// challenge. + /// The created for the response. + [NonAction] + public virtual ForbidResult Forbid(AuthenticationProperties properties) + => new ForbidResult(properties); + + /// + /// Creates a with the specified specified authentication scheme and + /// . + /// + /// The authentication scheme to challenge. + /// used to perform the authentication + /// challenge. + /// The created for the response. + [NonAction] + public virtual ForbidResult Forbid(string authenticationScheme, AuthenticationProperties properties) + => new ForbidResult(authenticationScheme, properties); + + /// + /// Creates a with the specified specified authentication schemes and + /// . + /// + /// The authentication schemes to challenge. + /// used to perform the authentication + /// challenge. + /// The created for the response. + [NonAction] + public virtual ForbidResult Forbid(IList authenticationSchemes, AuthenticationProperties properties) + => new ForbidResult(authenticationSchemes, properties); + + /// + /// Updates the specified instance using values from the controller's current + /// . + /// + /// The type of the model object. + /// The model instance to update. + /// A that on completion returns true if the update is successful. + [NonAction] + public virtual Task TryUpdateModelAsync( + TModel model) + where TModel : class + { + if (model == null) + { + throw new ArgumentNullException(nameof(model)); + } + + return TryUpdateModelAsync(model, prefix: string.Empty); + } + + /// + /// Updates the specified instance using values from the controller's current + /// and a . + /// + /// The type of the model object. + /// The model instance to update. + /// The prefix to use when looking up values in the current . + /// + /// A that on completion returns true if the update is successful. + [NonAction] + public virtual Task TryUpdateModelAsync( + TModel model, + string prefix) + where TModel : class + { + if (model == null) + { + throw new ArgumentNullException(nameof(model)); + } + + if (prefix == null) + { + throw new ArgumentNullException(nameof(prefix)); + } + + return TryUpdateModelAsync(model, prefix, new CompositeValueProvider(ControllerContext.ValueProviders)); + } + + /// + /// Updates the specified instance using the and a + /// . + /// + /// The type of the model object. + /// The model instance to update. + /// The prefix to use when looking up values in the . + /// + /// The used for looking up values. + /// A that on completion returns true if the update is successful. + [NonAction] + public virtual Task TryUpdateModelAsync( + TModel model, + string prefix, + IValueProvider valueProvider) + where TModel : class + { + if (model == null) + { + throw new ArgumentNullException(nameof(model)); + } + + if (prefix == null) + { + throw new ArgumentNullException(nameof(prefix)); + } + + if (valueProvider == null) + { + throw new ArgumentNullException(nameof(valueProvider)); + } + + return ModelBindingHelper.TryUpdateModelAsync( + model, + prefix, + ControllerContext, + MetadataProvider, + new CompositeModelBinder(ControllerContext.ModelBinders), + valueProvider, + ControllerContext.InputFormatters, + ObjectValidator, + new CompositeModelValidatorProvider(ControllerContext.ValidatorProviders)); + } + + /// + /// Updates the specified instance using values from the controller's current + /// and a . + /// + /// The type of the model object. + /// The model instance to update. + /// The prefix to use when looking up values in the current . + /// + /// (s) which represent top-level properties + /// which need to be included for the current model. + /// A that on completion returns true if the update is successful. + [NonAction] + public Task TryUpdateModelAsync( + TModel model, + string prefix, + params Expression>[] includeExpressions) + where TModel : class + { + if (model == null) + { + throw new ArgumentNullException(nameof(model)); + } + + if (includeExpressions == null) + { + throw new ArgumentNullException(nameof(includeExpressions)); + } + + return ModelBindingHelper.TryUpdateModelAsync( + model, + prefix, + ControllerContext, + MetadataProvider, + new CompositeModelBinder(ControllerContext.ModelBinders), + new CompositeValueProvider(ControllerContext.ValueProviders), + ControllerContext.InputFormatters, + ObjectValidator, + new CompositeModelValidatorProvider(ControllerContext.ValidatorProviders), + includeExpressions); + } + + /// + /// Updates the specified instance using values from the controller's current + /// and a . + /// + /// The type of the model object. + /// The model instance to update. + /// The prefix to use when looking up values in the current . + /// + /// A predicate which can be used to filter properties at runtime. + /// A that on completion returns true if the update is successful. + [NonAction] + public Task TryUpdateModelAsync( + TModel model, + string prefix, + Func predicate) + where TModel : class + { + if (model == null) + { + throw new ArgumentNullException(nameof(model)); + } + + if (predicate == null) + { + throw new ArgumentNullException(nameof(predicate)); + } + + return ModelBindingHelper.TryUpdateModelAsync( + model, + prefix, + ControllerContext, + MetadataProvider, + new CompositeModelBinder(ControllerContext.ModelBinders), + new CompositeValueProvider(ControllerContext.ValueProviders), + ControllerContext.InputFormatters, + ObjectValidator, + new CompositeModelValidatorProvider(ControllerContext.ValidatorProviders), + predicate); + } + + /// + /// Updates the specified instance using the and a + /// . + /// + /// The type of the model object. + /// The model instance to update. + /// The prefix to use when looking up values in the . + /// + /// The used for looking up values. + /// (s) which represent top-level properties + /// which need to be included for the current model. + /// A that on completion returns true if the update is successful. + [NonAction] + public Task TryUpdateModelAsync( + TModel model, + string prefix, + IValueProvider valueProvider, + params Expression>[] includeExpressions) + where TModel : class + { + if (model == null) + { + throw new ArgumentNullException(nameof(model)); + } + + if (valueProvider == null) + { + throw new ArgumentNullException(nameof(valueProvider)); + } + + if (includeExpressions == null) + { + throw new ArgumentNullException(nameof(includeExpressions)); + } + + return ModelBindingHelper.TryUpdateModelAsync( + model, + prefix, + ControllerContext, + MetadataProvider, + new CompositeModelBinder(ControllerContext.ModelBinders), + valueProvider, + ControllerContext.InputFormatters, + ObjectValidator, + new CompositeModelValidatorProvider(ControllerContext.ValidatorProviders), + includeExpressions); + } + + /// + /// Updates the specified instance using the and a + /// . + /// + /// The type of the model object. + /// The model instance to update. + /// The prefix to use when looking up values in the . + /// + /// The used for looking up values. + /// A predicate which can be used to filter properties at runtime. + /// A that on completion returns true if the update is successful. + [NonAction] + public Task TryUpdateModelAsync( + TModel model, + string prefix, + IValueProvider valueProvider, + Func predicate) + where TModel : class + { + if (model == null) + { + throw new ArgumentNullException(nameof(model)); + } + + if (valueProvider == null) + { + throw new ArgumentNullException(nameof(valueProvider)); + } + + if (predicate == null) + { + throw new ArgumentNullException(nameof(predicate)); + } + + return ModelBindingHelper.TryUpdateModelAsync( + model, + prefix, + ControllerContext, + MetadataProvider, + new CompositeModelBinder(ControllerContext.ModelBinders), + valueProvider, + ControllerContext.InputFormatters, + ObjectValidator, + new CompositeModelValidatorProvider(ControllerContext.ValidatorProviders), + predicate); + } + + /// + /// Updates the specified instance using values from the controller's current + /// and a . + /// + /// The model instance to update. + /// The type of model instance to update. + /// The prefix to use when looking up values in the current . + /// + /// A that on completion returns true if the update is successful. + [NonAction] + public virtual Task TryUpdateModelAsync( + object model, + Type modelType, + string prefix) + { + if (model == null) + { + throw new ArgumentNullException(nameof(model)); + } + + if (modelType == null) + { + throw new ArgumentNullException(nameof(modelType)); + } + + return ModelBindingHelper.TryUpdateModelAsync( + model, + modelType, + prefix, + ControllerContext, + MetadataProvider, + new CompositeModelBinder(ControllerContext.ModelBinders), + new CompositeValueProvider(ControllerContext.ValueProviders), + ControllerContext.InputFormatters, + ObjectValidator, + new CompositeModelValidatorProvider(ControllerContext.ValidatorProviders)); + } + + /// + /// Updates the specified instance using the and a + /// . + /// + /// The model instance to update. + /// The type of model instance to update. + /// The prefix to use when looking up values in the . + /// + /// The used for looking up values. + /// A predicate which can be used to filter properties at runtime. + /// A that on completion returns true if the update is successful. + [NonAction] + public Task TryUpdateModelAsync( + object model, + Type modelType, + string prefix, + IValueProvider valueProvider, + Func predicate) + { + if (model == null) + { + throw new ArgumentNullException(nameof(model)); + } + + if (modelType == null) + { + throw new ArgumentNullException(nameof(modelType)); + } + + if (valueProvider == null) + { + throw new ArgumentNullException(nameof(valueProvider)); + } + + if (predicate == null) + { + throw new ArgumentNullException(nameof(predicate)); + } + + return ModelBindingHelper.TryUpdateModelAsync( + model, + modelType, + prefix, + ControllerContext, + MetadataProvider, + new CompositeModelBinder(ControllerContext.ModelBinders), + valueProvider, + ControllerContext.InputFormatters, + ObjectValidator, + new CompositeModelValidatorProvider(ControllerContext.ValidatorProviders), + predicate); + } + + /// + /// Validates the specified instance. + /// + /// The model to validate. + /// true if the is valid; false otherwise. + [NonAction] + public virtual bool TryValidateModel( + object model) + { + if (model == null) + { + throw new ArgumentNullException(nameof(model)); + } + + return TryValidateModel(model, prefix: null); + } + + /// + /// Validates the specified instance. + /// + /// The model to validate. + /// The key to use when looking up information in . + /// + /// true if the is valid;false otherwise. + [NonAction] + public virtual bool TryValidateModel( + object model, + string prefix) + { + if (model == null) + { + throw new ArgumentNullException(nameof(model)); + } + + var modelName = prefix ?? string.Empty; + + // Clear ModelStateDictionary entries for the model so that it will be re-validated. + ModelBindingHelper.ClearValidationStateForModel( + model.GetType(), + ModelState, + MetadataProvider, + modelName); + + ObjectValidator.Validate( + ControllerContext, + new CompositeModelValidatorProvider(ControllerContext.ValidatorProviders), + validationState: null, + prefix: prefix, + model: model); + return ModelState.IsValid; + } + } +} diff --git a/src/Microsoft.AspNet.Mvc.ViewFeatures/Controller.cs b/src/Microsoft.AspNet.Mvc.ViewFeatures/Controller.cs index c7337f907b..297b254f34 100644 --- a/src/Microsoft.AspNet.Mvc.ViewFeatures/Controller.cs +++ b/src/Microsoft.AspNet.Mvc.ViewFeatures/Controller.cs @@ -2,224 +2,24 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Collections.Generic; -using System.IO; -using System.Linq.Expressions; -using System.Security.Claims; -using System.Text; using System.Threading.Tasks; -using Microsoft.AspNet.Http; -using Microsoft.AspNet.Http.Authentication; using Microsoft.AspNet.Mvc.Filters; using Microsoft.AspNet.Mvc.ModelBinding; -using Microsoft.AspNet.Mvc.ModelBinding.Validation; -using Microsoft.AspNet.Mvc.Routing; using Microsoft.AspNet.Mvc.ViewFeatures; -using Microsoft.AspNet.Routing; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Internal; -using Microsoft.Net.Http.Headers; using Newtonsoft.Json; namespace Microsoft.AspNet.Mvc { /// - /// Base class for an MVC controller. + /// A base class for an MVC controller with view support. /// - public abstract class Controller : IActionFilter, IAsyncActionFilter, IDisposable + public abstract class Controller : ControllerBase, IActionFilter, IAsyncActionFilter, IDisposable { - private ControllerContext _controllerContext; - private IModelMetadataProvider _metadataProvider; - private IObjectModelValidator _objectValidator; private ITempDataDictionary _tempData; - private IUrlHelper _url; private DynamicViewData _viewBag; private ViewDataDictionary _viewData; - /// - /// Gets the request-specific . - /// - public IServiceProvider Resolver - { - get - { - return HttpContext?.RequestServices; - } - } - - /// - /// Gets the for the executing action. - /// - public HttpContext HttpContext - { - get - { - return ControllerContext.HttpContext; - } - } - - /// - /// Gets the for the executing action. - /// - public HttpRequest Request - { - get - { - return HttpContext?.Request; - } - } - - /// - /// Gets the for the executing action. - /// - public HttpResponse Response - { - get - { - return HttpContext?.Response; - } - } - - /// - /// Gets the for the executing action. - /// - public RouteData RouteData - { - get - { - return ControllerContext.RouteData; - } - } - - /// - /// Gets the that contains the state of the model and of model-binding validation. - /// - public ModelStateDictionary ModelState - { - get - { - return ControllerContext?.ModelState; - } - } - - /// - /// Gets or sets the . - /// - /// - /// activates this property while activating controllers. - /// If user code directly instantiates a controller, the getter returns an empty - /// . - /// - [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 or sets the . - /// - public IModelMetadataProvider MetadataProvider - { - get - { - if (_metadataProvider == null) - { - _metadataProvider = Resolver?.GetRequiredService(); - } - - return _metadataProvider; - } - set - { - if (value == null) - { - throw new ArgumentNullException(nameof(value)); - } - - _metadataProvider = value; - } - } - - /// - /// Gets or sets the . - /// - public IUrlHelper Url - { - get - { - if (_url == null) - { - var factory = Resolver?.GetRequiredService(); - _url = factory?.GetUrlHelper(ControllerContext); - } - - return _url; - } - set - { - if (value == null) - { - throw new ArgumentNullException(nameof(value)); - } - - _url = value; - } - } - - /// - /// Gets or sets the . - /// - public IObjectModelValidator ObjectValidator - { - get - { - if (_objectValidator == null) - { - _objectValidator = Resolver?.GetRequiredService(); - } - - return _objectValidator; - } - set - { - if (value == null) - { - throw new ArgumentNullException(nameof(value)); - } - - _objectValidator = value; - } - } - - /// - /// Gets or sets the for user associated with the executing action. - /// - public ClaimsPrincipal User - { - get - { - return HttpContext?.User; - } - } - /// /// Gets or sets used by and . /// @@ -245,8 +45,7 @@ namespace Microsoft.AspNet.Mvc { if (value == null) { - throw - new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(ViewData)); + throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(ViewData)); } _viewData = value; @@ -449,80 +248,6 @@ namespace Microsoft.AspNet.Mvc }; } - /// - /// Creates a object by specifying a string. - /// - /// The content to write to the response. - /// The created object for the response. - [NonAction] - public virtual ContentResult Content(string content) - { - return Content(content, (MediaTypeHeaderValue)null); - } - - /// - /// Creates a object by specifying a string - /// and a content type. - /// - /// The content to write to the response. - /// The content type (MIME type). - /// The created object for the response. - [NonAction] - public virtual ContentResult Content(string content, string contentType) - { - return Content(content, MediaTypeHeaderValue.Parse(contentType)); - } - - /// - /// Creates a object by specifying a string, - /// a , and . - /// - /// The content to write to the response. - /// The content type (MIME type). - /// The content encoding. - /// The created object for the response. - /// - /// If encoding is provided by both the 'charset' and the parameters, then - /// the parameter is chosen as the final encoding. - /// - [NonAction] - public virtual ContentResult Content(string content, string contentType, Encoding contentEncoding) - { - var mediaTypeHeaderValue = MediaTypeHeaderValue.Parse(contentType); - mediaTypeHeaderValue.Encoding = contentEncoding ?? mediaTypeHeaderValue.Encoding; - return Content(content, mediaTypeHeaderValue); - } - - /// - /// Creates a object by specifying a - /// string and a . - /// - /// The content to write to the response. - /// The content type (MIME type). - /// The created object for the response. - [NonAction] - public virtual ContentResult Content(string content, MediaTypeHeaderValue contentType) - { - var result = new ContentResult - { - Content = content, - ContentType = contentType - }; - - return result; - } - - - /// - /// Creates a object that produces an empty No Content (204) response. - /// - /// The created object for the response. - [NonAction] - public virtual NoContentResult NoContent() - { - return new NoContentResult(); - } - /// /// Creates a object that serializes the specified object /// to JSON. @@ -570,755 +295,6 @@ namespace Microsoft.AspNet.Mvc return new JsonResult(data, serializerSettings); } - /// - /// Creates a object that produces an empty OK (200) response. - /// - /// The created for the response. - [NonAction] - public virtual HttpOkResult Ok() - { - return new HttpOkResult(); - } - - /// - /// Creates an object that produces an OK (200) response. - /// - /// The content value to format in the entity body. - /// The created for the response. - [NonAction] - public virtual HttpOkObjectResult Ok(object value) - { - var disposableValue = value as IDisposable; - if (disposableValue != null) - { - Response.RegisterForDispose(disposableValue); - } - - return new HttpOkObjectResult(value); - } - - /// - /// Creates a object that redirects to the specified . - /// - /// The URL to redirect to. - /// The created for the response. - [NonAction] - public virtual RedirectResult Redirect(string url) - { - if (string.IsNullOrEmpty(url)) - { - throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(url)); - } - - return new RedirectResult(url); - } - - /// - /// Creates a object with set to true - /// using the specified . - /// - /// The URL to redirect to. - /// The created for the response. - [NonAction] - public virtual RedirectResult RedirectPermanent(string url) - { - if (string.IsNullOrEmpty(url)) - { - throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(url)); - } - - return new RedirectResult(url, permanent: true); - } - - /// - /// Creates a object that redirects to - /// the specified local . - /// - /// The local URL to redirect to. - /// The created for the response. - [NonAction] - public virtual LocalRedirectResult LocalRedirect(string localUrl) - { - if (string.IsNullOrEmpty(localUrl)) - { - throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(localUrl)); - } - - return new LocalRedirectResult(localUrl); - } - - /// - /// Creates a object with - /// set to true using the specified . - /// - /// The local URL to redirect to. - /// The created for the response. - [NonAction] - public virtual LocalRedirectResult LocalRedirectPermanent(string localUrl) - { - if (string.IsNullOrEmpty(localUrl)) - { - throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(localUrl)); - } - - return new LocalRedirectResult(localUrl, permanent: true); - } - - /// - /// Redirects to the specified action using the . - /// - /// The name of the action. - /// The created for the response. - [NonAction] - public virtual RedirectToActionResult RedirectToAction(string actionName) - { - return RedirectToAction(actionName, routeValues: null); - } - - /// - /// Redirects to the specified action using the - /// and . - /// - /// The name of the action. - /// The parameters for a route. - /// The created for the response. - [NonAction] - public virtual RedirectToActionResult RedirectToAction(string actionName, object routeValues) - { - return RedirectToAction(actionName, controllerName: null, routeValues: routeValues); - } - - /// - /// Redirects to the specified action using the - /// and the . - /// - /// The name of the action. - /// The name of the controller. - /// The created for the response. - [NonAction] - public virtual RedirectToActionResult RedirectToAction(string actionName, string controllerName) - { - return RedirectToAction(actionName, controllerName, routeValues: null); - } - - /// - /// Redirects to the specified action using the specified , - /// , and . - /// - /// The name of the action. - /// The name of the controller. - /// The parameters for a route. - /// The created for the response. - [NonAction] - public virtual RedirectToActionResult RedirectToAction( - string actionName, - string controllerName, - object routeValues) - { - return new RedirectToActionResult(actionName, controllerName, routeValues) - { - UrlHelper = Url, - }; - } - - /// - /// Redirects to the specified action with set to true - /// using the specified . - /// - /// The name of the action. - /// The created for the response. - [NonAction] - public virtual RedirectToActionResult RedirectToActionPermanent(string actionName) - { - return RedirectToActionPermanent(actionName, routeValues: null); - } - - /// - /// Redirects to the specified action with set to true - /// using the specified and . - /// - /// The name of the action. - /// The parameters for a route. - /// The created for the response. - [NonAction] - public virtual RedirectToActionResult RedirectToActionPermanent(string actionName, object routeValues) - { - return RedirectToActionPermanent(actionName, controllerName: null, routeValues: routeValues); - } - - /// - /// Redirects to the specified action with set to true - /// using the specified and . - /// - /// The name of the action. - /// The name of the controller. - /// The created for the response. - [NonAction] - public virtual RedirectToActionResult RedirectToActionPermanent(string actionName, string controllerName) - { - return RedirectToActionPermanent(actionName, controllerName, routeValues: null); - } - - /// - /// Redirects to the specified action with set to true - /// using the specified , , - /// and . - /// - /// The name of the action. - /// The name of the controller. - /// The parameters for a route. - /// The created for the response. - [NonAction] - public virtual RedirectToActionResult RedirectToActionPermanent( - string actionName, - string controllerName, - object routeValues) - { - return new RedirectToActionResult( - actionName, - controllerName, - routeValues, - permanent: true) - { - UrlHelper = Url, - }; - } - - /// - /// Redirects to the specified route using the specified . - /// - /// The name of the route. - /// The created for the response. - [NonAction] - public virtual RedirectToRouteResult RedirectToRoute(string routeName) - { - return RedirectToRoute(routeName, routeValues: null); - } - - /// - /// Redirects to the specified route using the specified . - /// - /// The parameters for a route. - /// The created for the response. - [NonAction] - public virtual RedirectToRouteResult RedirectToRoute(object routeValues) - { - return RedirectToRoute(routeName: null, routeValues: routeValues); - } - - /// - /// Redirects to the specified route using the specified - /// and . - /// - /// The name of the route. - /// The parameters for a route. - /// The created for the response. - [NonAction] - public virtual RedirectToRouteResult RedirectToRoute(string routeName, object routeValues) - { - return new RedirectToRouteResult(routeName, routeValues) - { - UrlHelper = Url, - }; - } - - /// - /// Redirects to the specified route with set to true - /// using the specified . - /// - /// The name of the route. - /// The created for the response. - [NonAction] - public virtual RedirectToRouteResult RedirectToRoutePermanent(string routeName) - { - return RedirectToRoutePermanent(routeName, routeValues: null); - } - - /// - /// Redirects to the specified route with set to true - /// using the specified . - /// - /// The parameters for a route. - /// The created for the response. - [NonAction] - public virtual RedirectToRouteResult RedirectToRoutePermanent(object routeValues) - { - return RedirectToRoutePermanent(routeName: null, routeValues: routeValues); - } - - /// - /// Redirects to the specified route with set to true - /// using the specified and . - /// - /// The name of the route. - /// The parameters for a route. - /// The created for the response. - [NonAction] - public virtual RedirectToRouteResult RedirectToRoutePermanent(string routeName, object routeValues) - { - return new RedirectToRouteResult(routeName, routeValues, permanent: true) - { - UrlHelper = Url, - }; - } - - /// - /// Returns a file with the specified as content and the - /// specified as the Content-Type. - /// - /// The file contents. - /// The Content-Type of the file. - /// The created for the response. - [NonAction] - public virtual FileContentResult File(byte[] fileContents, string contentType) - { - return File(fileContents, contentType, fileDownloadName: null); - } - - /// - /// Returns a file with the specified as content, the - /// specified as the Content-Type and the - /// specified as the suggested file name. - /// - /// The file contents. - /// The Content-Type of the file. - /// The suggested file name. - /// The created for the response. - [NonAction] - public virtual FileContentResult File(byte[] fileContents, string contentType, string fileDownloadName) - { - return new FileContentResult(fileContents, contentType) { FileDownloadName = fileDownloadName }; - } - - /// - /// Returns a file in the specified with the - /// specified as the Content-Type. - /// - /// The with the contents of the file. - /// The Content-Type of the file. - /// The created for the response. - [NonAction] - public virtual FileStreamResult File(Stream fileStream, string contentType) - { - return File(fileStream, contentType, fileDownloadName: null); - } - - /// - /// Returns a file in the specified with the - /// specified as the Content-Type and the - /// specified as the suggested file name. - /// - /// The with the contents of the file. - /// The Content-Type of the file. - /// The suggested file name. - /// The created for the response. - [NonAction] - public virtual FileStreamResult File(Stream fileStream, string contentType, string fileDownloadName) - { - if (fileStream != null) - { - Response.RegisterForDispose(fileStream); - } - - return new FileStreamResult(fileStream, contentType) { FileDownloadName = fileDownloadName }; - } - - /// - /// Returns the file specified by with the - /// specified as the Content-Type. - /// - /// The virtual path of the file to be returned. - /// The Content-Type of the file. - /// The created for the response. - [NonAction] - public virtual VirtualFileResult File(string virtualPath, string contentType) - { - return File(virtualPath, contentType, fileDownloadName: null); - } - - /// - /// Returns the file specified by with the - /// specified as the Content-Type and the - /// specified as the suggested file name. - /// - /// The virtual path of the file to be returned. - /// The Content-Type of the file. - /// The suggested file name. - /// The created for the response. - [NonAction] - public virtual VirtualFileResult File(string virtualPath, string contentType, string fileDownloadName) - { - return new VirtualFileResult(virtualPath, contentType) { FileDownloadName = fileDownloadName }; - } - - /// - /// Returns the file specified by with the - /// specified as the Content-Type. - /// - /// The physical path of the file to be returned. - /// The Content-Type of the file. - /// The created for the response. - [NonAction] - public virtual PhysicalFileResult PhysicalFile(string physicalPath, string contentType) - { - return PhysicalFile(physicalPath, contentType, fileDownloadName: null); - } - - /// - /// Returns the file specified by with the - /// specified as the Content-Type and the - /// specified as the suggested file name. - /// - /// The physical path of the file to be returned. - /// The Content-Type of the file. - /// The suggested file name. - /// The created for the response. - [NonAction] - public virtual PhysicalFileResult PhysicalFile( - string physicalPath, - string contentType, - string fileDownloadName) - { - return new PhysicalFileResult(physicalPath, contentType) { FileDownloadName = fileDownloadName }; - } - - /// - /// Creates an that produces an Unauthorized (401) response. - /// - /// The created for the response. - [NonAction] - public virtual HttpUnauthorizedResult HttpUnauthorized() - { - return new HttpUnauthorizedResult(); - } - - /// - /// Creates an that produces a Not Found (404) response. - /// - /// The created for the response. - [NonAction] - public virtual HttpNotFoundResult HttpNotFound() - { - return new HttpNotFoundResult(); - } - - /// - /// Creates an that produces a Not Found (404) response. - /// - /// The created for the response. - [NonAction] - public virtual HttpNotFoundObjectResult HttpNotFound(object value) - { - var disposableValue = value as IDisposable; - if (disposableValue != null) - { - Response.RegisterForDispose(disposableValue); - } - - return new HttpNotFoundObjectResult(value); - } - - /// - /// Creates an that produces a Bad Request (400) response. - /// - /// The created for the response. - [NonAction] - public virtual BadRequestResult HttpBadRequest() - { - return new BadRequestResult(); - } - - /// - /// Creates an that produces a Bad Request (400) response. - /// - /// The created for the response. - [NonAction] - public virtual BadRequestObjectResult HttpBadRequest(object error) - { - var disposableValue = error as IDisposable; - if (disposableValue != null) - { - Response.RegisterForDispose(disposableValue); - } - - return new BadRequestObjectResult(error); - } - - /// - /// Creates an that produces a Bad Request (400) response. - /// - /// The created for the response. - [NonAction] - public virtual BadRequestObjectResult HttpBadRequest(ModelStateDictionary modelState) - { - if (modelState == null) - { - throw new ArgumentNullException(nameof(modelState)); - } - - return new BadRequestObjectResult(modelState); - } - - /// - /// Creates a object that produces a Created (201) response. - /// - /// The URI at which the content has been created. - /// The content value to format in the entity body. - /// The created for the response. - [NonAction] - public virtual CreatedResult Created(string uri, object value) - { - if (uri == null) - { - throw new ArgumentNullException(nameof(uri)); - } - - var disposableValue = value as IDisposable; - if (disposableValue != null) - { - Response.RegisterForDispose(disposableValue); - } - - return new CreatedResult(uri, value); - } - - /// - /// Creates a object that produces a Created (201) response. - /// - /// The URI at which the content has been created. - /// The content value to format in the entity body. - /// The created for the response. - [NonAction] - public virtual CreatedResult Created(Uri uri, object value) - { - if (uri == null) - { - throw new ArgumentNullException(nameof(uri)); - } - - var disposableValue = value as IDisposable; - if (disposableValue != null) - { - Response.RegisterForDispose(disposableValue); - } - - return new CreatedResult(uri, value); - } - - /// - /// Creates a object that produces a Created (201) response. - /// - /// The name of the action to use for generating the URL. - /// The content value to format in the entity body. - /// The created for the response. - [NonAction] - public virtual CreatedAtActionResult CreatedAtAction(string actionName, object value) - { - return CreatedAtAction(actionName, routeValues: null, value: value); - } - - /// - /// Creates a object that produces a Created (201) response. - /// - /// The name of the action to use for generating the URL. - /// The route data to use for generating the URL. - /// The content value to format in the entity body. - /// The created for the response. - [NonAction] - public virtual CreatedAtActionResult CreatedAtAction(string actionName, object routeValues, object value) - { - return CreatedAtAction(actionName, controllerName: null, routeValues: routeValues, value: value); - } - - /// - /// Creates a object that produces a Created (201) response. - /// - /// The name of the action to use for generating the URL. - /// The name of the controller to use for generating the URL. - /// The route data to use for generating the URL. - /// The content value to format in the entity body. - /// The created for the response. - [NonAction] - public virtual CreatedAtActionResult CreatedAtAction( - string actionName, - string controllerName, - object routeValues, - object value) - { - var disposableValue = value as IDisposable; - if (disposableValue != null) - { - Response.RegisterForDispose(disposableValue); - } - - return new CreatedAtActionResult(actionName, controllerName, routeValues, value); - } - - /// - /// Creates a object that produces a Created (201) response. - /// - /// The name of the route to use for generating the URL. - /// The content value to format in the entity body. - /// The created for the response. - [NonAction] - public virtual CreatedAtRouteResult CreatedAtRoute(string routeName, object value) - { - return CreatedAtRoute(routeName, routeValues: null, value: value); - } - - /// - /// Creates a object that produces a Created (201) response. - /// - /// The route data to use for generating the URL. - /// The content value to format in the entity body. - /// The created for the response. - [NonAction] - public virtual CreatedAtRouteResult CreatedAtRoute(object routeValues, object value) - { - return CreatedAtRoute(routeName: null, routeValues: routeValues, value: value); - } - - /// - /// Creates a object that produces a Created (201) response. - /// - /// 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. - /// The created for the response. - [NonAction] - public virtual CreatedAtRouteResult CreatedAtRoute(string routeName, object routeValues, object value) - { - var disposableValue = value as IDisposable; - if (disposableValue != null) - { - Response.RegisterForDispose(disposableValue); - } - - return new CreatedAtRouteResult(routeName, routeValues, value); - } - - /// - /// Creates a . - /// - /// The created for the response. - [NonAction] - public virtual ChallengeResult Challenge() - => new ChallengeResult(); - - /// - /// Creates a with the specified authentication scheme. - /// - /// The authentication scheme to challenge. - /// The created for the response. - [NonAction] - public virtual ChallengeResult Challenge(string authenticationScheme) - => new ChallengeResult(authenticationScheme); - - /// - /// Creates a with the specified authentication schemes. - /// - /// The authentication schemes to challenge. - /// The created for the response. - [NonAction] - public virtual ChallengeResult Challenge(IList authenticationSchemes) - => new ChallengeResult(authenticationSchemes); - - /// - /// Creates a with the specified . - /// - /// used to perform the authentication - /// challenge. - /// The created for the response. - [NonAction] - public virtual ChallengeResult Challenge(AuthenticationProperties properties) - => new ChallengeResult(properties); - - /// - /// Creates a with the specified specified authentication scheme and - /// . - /// - /// The authentication scheme to challenge. - /// used to perform the authentication - /// challenge. - /// The created for the response. - [NonAction] - public virtual ChallengeResult Challenge(string authenticationScheme, AuthenticationProperties properties) - => new ChallengeResult(authenticationScheme, properties); - - /// - /// Creates a with the specified specified authentication schemes and - /// . - /// - /// The authentication schemes to challenge. - /// used to perform the authentication - /// challenge. - /// The created for the response. - [NonAction] - public virtual ChallengeResult Challenge( - IList authenticationSchemes, - AuthenticationProperties properties) - => new ChallengeResult(authenticationSchemes, properties); - - /// - /// Creates a . - /// - /// The created for the response. - [NonAction] - public virtual ForbidResult Forbid() - => new ForbidResult(); - - /// - /// Creates a with the specified authentication scheme. - /// - /// The authentication scheme to challenge. - /// The created for the response. - [NonAction] - public virtual ForbidResult Forbid(string authenticationScheme) - => new ForbidResult(authenticationScheme); - - /// - /// Creates a with the specified authentication schemes. - /// - /// The authentication schemes to challenge. - /// The created for the response. - [NonAction] - public virtual ForbidResult Forbid(IList authenticationSchemes) - => new ForbidResult(authenticationSchemes); - - /// - /// Creates a with the specified . - /// - /// used to perform the authentication - /// challenge. - /// The created for the response. - [NonAction] - public virtual ForbidResult Forbid(AuthenticationProperties properties) - => new ForbidResult(properties); - - /// - /// Creates a with the specified specified authentication scheme and - /// . - /// - /// The authentication scheme to challenge. - /// used to perform the authentication - /// challenge. - /// The created for the response. - [NonAction] - public virtual ForbidResult Forbid(string authenticationScheme, AuthenticationProperties properties) - => new ForbidResult(authenticationScheme, properties); - - /// - /// Creates a with the specified specified authentication schemes and - /// . - /// - /// The authentication schemes to challenge. - /// used to perform the authentication - /// challenge. - /// The created for the response. - [NonAction] - public virtual ForbidResult Forbid(IList authenticationSchemes, AuthenticationProperties properties) - => new ForbidResult(authenticationSchemes, properties); - /// /// Called before the action method is invoked. /// @@ -1366,417 +342,6 @@ namespace Microsoft.AspNet.Mvc } } - /// - /// Updates the specified instance using values from the controller's current - /// . - /// - /// The type of the model object. - /// The model instance to update. - /// A that on completion returns true if the update is successful. - [NonAction] - public virtual Task TryUpdateModelAsync( - TModel model) - where TModel : class - { - if (model == null) - { - throw new ArgumentNullException(nameof(model)); - } - - return TryUpdateModelAsync(model, prefix: string.Empty); - } - - /// - /// Updates the specified instance using values from the controller's current - /// and a . - /// - /// The type of the model object. - /// The model instance to update. - /// The prefix to use when looking up values in the current . - /// - /// A that on completion returns true if the update is successful. - [NonAction] - public virtual Task TryUpdateModelAsync( - TModel model, - string prefix) - where TModel : class - { - if (model == null) - { - throw new ArgumentNullException(nameof(model)); - } - - if (prefix == null) - { - throw new ArgumentNullException(nameof(prefix)); - } - - return TryUpdateModelAsync(model, prefix, new CompositeValueProvider(ControllerContext.ValueProviders)); - } - - /// - /// Updates the specified instance using the and a - /// . - /// - /// The type of the model object. - /// The model instance to update. - /// The prefix to use when looking up values in the . - /// - /// The used for looking up values. - /// A that on completion returns true if the update is successful. - [NonAction] - public virtual Task TryUpdateModelAsync( - TModel model, - string prefix, - IValueProvider valueProvider) - where TModel : class - { - if (model == null) - { - throw new ArgumentNullException(nameof(model)); - } - - if (prefix == null) - { - throw new ArgumentNullException(nameof(prefix)); - } - - if (valueProvider == null) - { - throw new ArgumentNullException(nameof(valueProvider)); - } - - return ModelBindingHelper.TryUpdateModelAsync( - model, - prefix, - ControllerContext, - MetadataProvider, - new CompositeModelBinder(ControllerContext.ModelBinders), - valueProvider, - ControllerContext.InputFormatters, - ObjectValidator, - new CompositeModelValidatorProvider(ControllerContext.ValidatorProviders)); - } - - /// - /// Updates the specified instance using values from the controller's current - /// and a . - /// - /// The type of the model object. - /// The model instance to update. - /// The prefix to use when looking up values in the current . - /// - /// (s) which represent top-level properties - /// which need to be included for the current model. - /// A that on completion returns true if the update is successful. - [NonAction] - public Task TryUpdateModelAsync( - TModel model, - string prefix, - params Expression>[] includeExpressions) - where TModel : class - { - if (model == null) - { - throw new ArgumentNullException(nameof(model)); - } - - if (includeExpressions == null) - { - throw new ArgumentNullException(nameof(includeExpressions)); - } - - return ModelBindingHelper.TryUpdateModelAsync( - model, - prefix, - ControllerContext, - MetadataProvider, - new CompositeModelBinder(ControllerContext.ModelBinders), - new CompositeValueProvider(ControllerContext.ValueProviders), - ControllerContext.InputFormatters, - ObjectValidator, - new CompositeModelValidatorProvider(ControllerContext.ValidatorProviders), - includeExpressions); - } - - /// - /// Updates the specified instance using values from the controller's current - /// and a . - /// - /// The type of the model object. - /// The model instance to update. - /// The prefix to use when looking up values in the current . - /// - /// A predicate which can be used to filter properties at runtime. - /// A that on completion returns true if the update is successful. - [NonAction] - public Task TryUpdateModelAsync( - TModel model, - string prefix, - Func predicate) - where TModel : class - { - if (model == null) - { - throw new ArgumentNullException(nameof(model)); - } - - if (predicate == null) - { - throw new ArgumentNullException(nameof(predicate)); - } - - return ModelBindingHelper.TryUpdateModelAsync( - model, - prefix, - ControllerContext, - MetadataProvider, - new CompositeModelBinder(ControllerContext.ModelBinders), - new CompositeValueProvider(ControllerContext.ValueProviders), - ControllerContext.InputFormatters, - ObjectValidator, - new CompositeModelValidatorProvider(ControllerContext.ValidatorProviders), - predicate); - } - - /// - /// Updates the specified instance using the and a - /// . - /// - /// The type of the model object. - /// The model instance to update. - /// The prefix to use when looking up values in the . - /// - /// The used for looking up values. - /// (s) which represent top-level properties - /// which need to be included for the current model. - /// A that on completion returns true if the update is successful. - [NonAction] - public Task TryUpdateModelAsync( - TModel model, - string prefix, - IValueProvider valueProvider, - params Expression>[] includeExpressions) - where TModel : class - { - if (model == null) - { - throw new ArgumentNullException(nameof(model)); - } - - if (valueProvider == null) - { - throw new ArgumentNullException(nameof(valueProvider)); - } - - if (includeExpressions == null) - { - throw new ArgumentNullException(nameof(includeExpressions)); - } - - return ModelBindingHelper.TryUpdateModelAsync( - model, - prefix, - ControllerContext, - MetadataProvider, - new CompositeModelBinder(ControllerContext.ModelBinders), - valueProvider, - ControllerContext.InputFormatters, - ObjectValidator, - new CompositeModelValidatorProvider(ControllerContext.ValidatorProviders), - includeExpressions); - } - - /// - /// Updates the specified instance using the and a - /// . - /// - /// The type of the model object. - /// The model instance to update. - /// The prefix to use when looking up values in the . - /// - /// The used for looking up values. - /// A predicate which can be used to filter properties at runtime. - /// A that on completion returns true if the update is successful. - [NonAction] - public Task TryUpdateModelAsync( - TModel model, - string prefix, - IValueProvider valueProvider, - Func predicate) - where TModel : class - { - if (model == null) - { - throw new ArgumentNullException(nameof(model)); - } - - if (valueProvider == null) - { - throw new ArgumentNullException(nameof(valueProvider)); - } - - if (predicate == null) - { - throw new ArgumentNullException(nameof(predicate)); - } - - return ModelBindingHelper.TryUpdateModelAsync( - model, - prefix, - ControllerContext, - MetadataProvider, - new CompositeModelBinder(ControllerContext.ModelBinders), - valueProvider, - ControllerContext.InputFormatters, - ObjectValidator, - new CompositeModelValidatorProvider(ControllerContext.ValidatorProviders), - predicate); - } - - /// - /// Updates the specified instance using values from the controller's current - /// and a . - /// - /// The model instance to update. - /// The type of model instance to update. - /// The prefix to use when looking up values in the current . - /// - /// A that on completion returns true if the update is successful. - [NonAction] - public virtual Task TryUpdateModelAsync( - object model, - Type modelType, - string prefix) - { - if (model == null) - { - throw new ArgumentNullException(nameof(model)); - } - - if (modelType == null) - { - throw new ArgumentNullException(nameof(modelType)); - } - - return ModelBindingHelper.TryUpdateModelAsync( - model, - modelType, - prefix, - ControllerContext, - MetadataProvider, - new CompositeModelBinder(ControllerContext.ModelBinders), - new CompositeValueProvider(ControllerContext.ValueProviders), - ControllerContext.InputFormatters, - ObjectValidator, - new CompositeModelValidatorProvider(ControllerContext.ValidatorProviders)); - } - - /// - /// Updates the specified instance using the and a - /// . - /// - /// The model instance to update. - /// The type of model instance to update. - /// The prefix to use when looking up values in the . - /// - /// The used for looking up values. - /// A predicate which can be used to filter properties at runtime. - /// A that on completion returns true if the update is successful. - [NonAction] - public Task TryUpdateModelAsync( - object model, - Type modelType, - string prefix, - IValueProvider valueProvider, - Func predicate) - { - if (model == null) - { - throw new ArgumentNullException(nameof(model)); - } - - if (modelType == null) - { - throw new ArgumentNullException(nameof(modelType)); - } - - if (valueProvider == null) - { - throw new ArgumentNullException(nameof(valueProvider)); - } - - if (predicate == null) - { - throw new ArgumentNullException(nameof(predicate)); - } - - return ModelBindingHelper.TryUpdateModelAsync( - model, - modelType, - prefix, - ControllerContext, - MetadataProvider, - new CompositeModelBinder(ControllerContext.ModelBinders), - valueProvider, - ControllerContext.InputFormatters, - ObjectValidator, - new CompositeModelValidatorProvider(ControllerContext.ValidatorProviders), - predicate); - } - - /// - /// Validates the specified instance. - /// - /// The model to validate. - /// true if the is valid; false otherwise. - [NonAction] - public virtual bool TryValidateModel( - object model) - { - if (model == null) - { - throw new ArgumentNullException(nameof(model)); - } - - return TryValidateModel(model, prefix: null); - } - - /// - /// Validates the specified instance. - /// - /// The model to validate. - /// The key to use when looking up information in . - /// - /// true if the is valid;false otherwise. - [NonAction] - public virtual bool TryValidateModel( - object model, - string prefix) - { - if (model == null) - { - throw new ArgumentNullException(nameof(model)); - } - - var modelName = prefix ?? string.Empty; - - // Clear ModelStateDictionary entries for the model so that it will be re-validated. - ModelBindingHelper.ClearValidationStateForModel( - model.GetType(), - ModelState, - MetadataProvider, - modelName); - - ObjectValidator.Validate( - ControllerContext, - new CompositeModelValidatorProvider(ControllerContext.ValidatorProviders), - validationState: null, - prefix: prefix, - model: model); - return ModelState.IsValid; - } - /// public void Dispose() { diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ControllerBaseTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ControllerBaseTest.cs new file mode 100644 index 0000000000..9ff25d1f3c --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ControllerBaseTest.cs @@ -0,0 +1,1699 @@ +// 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; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNet.Http; +using Microsoft.AspNet.Http.Internal; +using Microsoft.AspNet.Mvc.ModelBinding; +using Microsoft.AspNet.Mvc.ModelBinding.Validation; +using Microsoft.AspNet.Routing; +using Microsoft.AspNet.Testing; +using Microsoft.Net.Http.Headers; +using Moq; +using Xunit; + +namespace Microsoft.AspNet.Mvc.Core.Test +{ + public class ControllerBaseTest + { + public static IEnumerable PublicNormalMethodsFromControllerBase + { + get + { + return typeof(ControllerBase).GetTypeInfo() + .DeclaredMethods + .Where(method => method.IsPublic && + !method.IsSpecialName && + !method.Name.Equals("Dispose", StringComparison.OrdinalIgnoreCase)) + .Select(method => new[] { method }); + } + } + + [Fact] + public void Redirect_WithParameterUrl_SetsRedirectResultSameUrl() + { + // Arrange + var controller = new TestableController(); + var url = "/test/url"; + + // Act + var result = controller.Redirect(url); + + // Assert + Assert.IsType(result); + Assert.False(result.Permanent); + Assert.Same(url, result.Url); + } + + [Fact] + public void RedirectPermanent_WithParameterUrl_SetsRedirectResultPermanentAndSameUrl() + { + // Arrange + var controller = new TestableController(); + var url = "/test/url"; + + // Act + var result = controller.RedirectPermanent(url); + + // Assert + Assert.IsType(result); + Assert.True(result.Permanent); + Assert.Same(url, result.Url); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + public void Redirect_WithParameter_NullOrEmptyUrl_Throws(string url) + { + // Arrange + var controller = new TestableController(); + + // Act & Assert + ExceptionAssert.ThrowsArgumentNullOrEmpty( + () => controller.Redirect(url: url), "url"); + } + + [Fact] + public void LocalRedirect_WithParameterUrl_SetsLocalRedirectResultWithSameUrl() + { + // Arrange + var controller = new TestableController(); + var url = "/test/url"; + + // Act + var result = controller.LocalRedirect(url); + + // Assert + Assert.IsType(result); + Assert.False(result.Permanent); + Assert.Same(url, result.Url); + } + + [Fact] + public void LocalRedirectPermanent_WithParameterUrl_SetsLocalRedirectResultPermanentWithSameUrl() + { + // Arrange + var controller = new TestableController(); + var url = "/test/url"; + + // Act + var result = controller.LocalRedirectPermanent(url); + + // Assert + Assert.IsType(result); + Assert.True(result.Permanent); + Assert.Same(url, result.Url); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + public void LocalRedirect_WithParameter_NullOrEmptyUrl_Throws(string url) + { + // Arrange + var controller = new TestableController(); + + // Act & Assert + ExceptionAssert.ThrowsArgumentNullOrEmpty( + () => controller.LocalRedirect(localUrl: url), "localUrl"); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + public void RedirectPermanent_WithParameter_NullOrEmptyUrl_Throws(string url) + { + // Arrange + var controller = new TestableController(); + + // Act & Assert + ExceptionAssert.ThrowsArgumentNullOrEmpty( + () => controller.RedirectPermanent(url: url), "url"); + } + + [Fact] + public void RedirectToAction_WithParameterActionName_SetsResultActionName() + { + // Arrange + var controller = new TestableController(); + + // Act + var resultTemporary = controller.RedirectToAction("SampleAction"); + + // Assert + Assert.IsType(resultTemporary); + Assert.False(resultTemporary.Permanent); + Assert.Equal("SampleAction", resultTemporary.ActionName); + } + + [Fact] + public void RedirectToActionPermanent_WithParameterActionName_SetsResultActionNameAndPermanent() + { + // Arrange + var controller = new TestableController(); + + // Act + var resultPermanent = controller.RedirectToActionPermanent("SampleAction"); + + // Assert + Assert.IsType(resultPermanent); + Assert.True(resultPermanent.Permanent); + Assert.Equal("SampleAction", resultPermanent.ActionName); + } + + [Theory] + [InlineData("")] + [InlineData(null)] + [InlineData("SampleController")] + public void RedirectToAction_WithParameterActionAndControllerName_SetsEqualNames(string controllerName) + { + // Arrange + var controller = new TestableController(); + + // Act + var resultTemporary = controller.RedirectToAction("SampleAction", controllerName); + + // Assert + Assert.IsType(resultTemporary); + Assert.False(resultTemporary.Permanent); + Assert.Equal("SampleAction", resultTemporary.ActionName); + Assert.Equal(controllerName, resultTemporary.ControllerName); + } + + [Theory] + [InlineData("")] + [InlineData(null)] + [InlineData("SampleController")] + public void RedirectToActionPermanent_WithParameterActionAndControllerName_SetsEqualNames( + string controllerName) + { + // Arrange + var controller = new TestableController(); + + // Act + var resultPermanent = controller.RedirectToActionPermanent("SampleAction", controllerName); + + // Assert + Assert.IsType(resultPermanent); + Assert.True(resultPermanent.Permanent); + Assert.Equal("SampleAction", resultPermanent.ActionName); + Assert.Equal(controllerName, resultPermanent.ControllerName); + } + + [Theory] + [MemberData(nameof(RedirectTestData))] + public void RedirectToAction_WithParameterActionControllerRouteValues_SetsResultProperties( + object routeValues, + IEnumerable> expected) + { + // Arrange + var controller = new TestableController(); + + // Act + var resultTemporary = controller.RedirectToAction("SampleAction", "SampleController", routeValues); + + // Assert + Assert.IsType(resultTemporary); + Assert.False(resultTemporary.Permanent); + Assert.Equal("SampleAction", resultTemporary.ActionName); + Assert.Equal("SampleController", resultTemporary.ControllerName); + Assert.Equal(expected, resultTemporary.RouteValues); + } + + [Theory] + [MemberData(nameof(RedirectTestData))] + public void RedirectToActionPermanent_WithParameterActionControllerRouteValues_SetsResultProperties( + object routeValues, + IEnumerable> expected) + { + // Arrange + var controller = new TestableController(); + + // Act + var resultPermanent = controller.RedirectToActionPermanent( + "SampleAction", + "SampleController", + routeValues); + + // Assert + Assert.IsType(resultPermanent); + Assert.True(resultPermanent.Permanent); + Assert.Equal("SampleAction", resultPermanent.ActionName); + Assert.Equal("SampleController", resultPermanent.ControllerName); + Assert.Equal(expected, resultPermanent.RouteValues); + } + + [Theory] + [MemberData(nameof(RedirectTestData))] + public void RedirectToAction_WithParameterActionAndRouteValues_SetsResultProperties( + object routeValues, + IEnumerable> expected) + { + // Arrange + var controller = new TestableController(); + + // Act + var resultTemporary = controller.RedirectToAction(actionName: null, routeValues: routeValues); + + // Assert + Assert.IsType(resultTemporary); + Assert.False(resultTemporary.Permanent); + Assert.Null(resultTemporary.ActionName); + Assert.Equal(expected, resultTemporary.RouteValues); + } + + [Theory] + [MemberData(nameof(RedirectTestData))] + public void RedirectToActionPermanent_WithParameterActionAndRouteValues_SetsResultProperties( + object routeValues, + IEnumerable> expected) + { + // Arrange + var controller = new TestableController(); + + // Act + var resultPermanent = controller.RedirectToActionPermanent(null, routeValues); + + // Assert + Assert.IsType(resultPermanent); + Assert.True(resultPermanent.Permanent); + Assert.Null(resultPermanent.ActionName); + Assert.Equal(expected, resultPermanent.RouteValues); + } + + [Theory] + [MemberData(nameof(RedirectTestData))] + public void RedirectToRoute_WithParameterRouteValues_SetsResultEqualRouteValues( + object routeValues, + IEnumerable> expected) + { + // Arrange + var controller = new TestableController(); + + // Act + var resultTemporary = controller.RedirectToRoute(routeValues); + + // Assert + Assert.IsType(resultTemporary); + Assert.False(resultTemporary.Permanent); + Assert.Equal(expected, resultTemporary.RouteValues); + } + + [Theory] + [MemberData(nameof(RedirectTestData))] + public void RedirectToRoutePermanent_WithParameterRouteValues_SetsResultEqualRouteValuesAndPermanent( + object routeValues, + IEnumerable> expected) + { + // Arrange + var controller = new TestableController(); + + // Act + var resultPermanent = controller.RedirectToRoutePermanent(routeValues); + + // Assert + Assert.IsType(resultPermanent); + Assert.True(resultPermanent.Permanent); + Assert.Equal(expected, resultPermanent.RouteValues); + } + + [Fact] + public void RedirectToRoute_WithParameterRouteName_SetsResultSameRouteName() + { + // Arrange + var controller = new TestableController(); + var routeName = "CustomRouteName"; + + // Act + var resultTemporary = controller.RedirectToRoute(routeName); + + // Assert + Assert.IsType(resultTemporary); + Assert.False(resultTemporary.Permanent); + Assert.Same(routeName, resultTemporary.RouteName); + } + + [Fact] + public void RedirectToRoutePermanent_WithParameterRouteName_SetsResultSameRouteNameAndPermanent() + { + // Arrange + var controller = new TestableController(); + var routeName = "CustomRouteName"; + + // Act + var resultPermanent = controller.RedirectToRoutePermanent(routeName); + + // Assert + Assert.IsType(resultPermanent); + Assert.True(resultPermanent.Permanent); + Assert.Same(routeName, resultPermanent.RouteName); + } + + [Theory] + [MemberData(nameof(RedirectTestData))] + public void RedirectToRoute_WithParameterRouteNameAndRouteValues_SetsResultSameRouteNameAndRouteValues( + object routeValues, + IEnumerable> expected) + { + // Arrange + var controller = new TestableController(); + var routeName = "CustomRouteName"; + + // Act + var resultTemporary = controller.RedirectToRoute(routeName, routeValues); + + // Assert + Assert.IsType(resultTemporary); + Assert.False(resultTemporary.Permanent); + Assert.Same(routeName, resultTemporary.RouteName); + Assert.Equal(expected, resultTemporary.RouteValues); + } + + [Theory] + [MemberData(nameof(RedirectTestData))] + public void RedirectToRoutePermanent_WithParameterRouteNameAndRouteValues_SetsResultProperties( + object routeValues, + IEnumerable> expected) + { + // Arrange + var controller = new TestableController(); + var routeName = "CustomRouteName"; + + // Act + var resultPermanent = controller.RedirectToRoutePermanent(routeName, routeValues); + + // Assert + Assert.IsType(resultPermanent); + Assert.True(resultPermanent.Permanent); + Assert.Same(routeName, resultPermanent.RouteName); + Assert.Equal(expected, resultPermanent.RouteValues); + } + + [Fact] + public void Created_WithStringParameter_SetsCreatedLocation() + { + // Arrange + var controller = new TestableController(); + var uri = "http://test/url"; + + // Act + var result = controller.Created(uri, null); + + // Assert + Assert.IsType(result); + Assert.Equal(StatusCodes.Status201Created, result.StatusCode); + Assert.Same(uri, result.Location); + } + + [Fact] + public void Created_WithAbsoluteUriParameter_SetsCreatedLocation() + { + // Arrange + var controller = new TestableController(); + var uri = new Uri("http://test/url"); + + // Act + var result = controller.Created(uri, null); + + // Assert + Assert.IsType(result); + Assert.Equal(StatusCodes.Status201Created, result.StatusCode); + Assert.Equal(uri.OriginalString, result.Location); + } + + [Fact] + public void Created_WithRelativeUriParameter_SetsCreatedLocation() + { + // Arrange + var controller = new TestableController(); + var uri = new Uri("/test/url", UriKind.Relative); + + // Act + var result = controller.Created(uri, null); + + // Assert + Assert.IsType(result); + Assert.Equal(StatusCodes.Status201Created, result.StatusCode); + Assert.Equal(uri.OriginalString, result.Location); + } + + [Fact] + public void Created_IDisposableObject_RegistersForDispose() + { + // Arrange + var mockHttpContext = new Mock(); + mockHttpContext.Setup(x => x.Response.RegisterForDispose(It.IsAny())); + var uri = new Uri("/test/url", UriKind.Relative); + + var controller = new TestableController(); + controller.ControllerContext.HttpContext = mockHttpContext.Object; + + var input = new DisposableObject(); + + // Act + var result = controller.Created(uri, input); + + // Assert + Assert.IsType(result); + Assert.Equal(StatusCodes.Status201Created, result.StatusCode); + Assert.Equal(uri.OriginalString, result.Location); + Assert.Same(input, result.Value); + mockHttpContext.Verify( + x => x.Response.RegisterForDispose(It.IsAny()), + Times.Once()); + } + + [Fact] + public void CreatedAtAction_WithParameterActionName_SetsResultActionName() + { + // Arrange + var controller = new TestableController(); + + // Act + var result = controller.CreatedAtAction("SampleAction", null); + + // Assert + Assert.IsType(result); + Assert.Equal(StatusCodes.Status201Created, result.StatusCode); + Assert.Equal("SampleAction", result.ActionName); + } + + [Theory] + [InlineData("")] + [InlineData(null)] + [InlineData("SampleController")] + public void CreatedAtAction_WithActionControllerAndNullRouteValue_SetsSameValue( + string controllerName) + { + // Arrange + var controller = new TestableController(); + + // Act + var result = controller.CreatedAtAction("SampleAction", controllerName, null, null); + + // Assert + Assert.IsType(result); + Assert.Equal(StatusCodes.Status201Created, result.StatusCode); + Assert.Equal("SampleAction", result.ActionName); + Assert.Equal(controllerName, result.ControllerName); + } + + [Fact] + public void CreatedAtAction_WithActionControllerRouteValues_SetsSameValues() + { + // Arrange + var controller = new TestableController(); + var expected = new Dictionary + { + { "test", "case" }, + { "sample", "route" }, + }; + + // Act + var result = controller.CreatedAtAction( + "SampleAction", + "SampleController", + new RouteValueDictionary(expected), null); + + // Assert + Assert.IsType(result); + Assert.Equal(StatusCodes.Status201Created, result.StatusCode); + Assert.Equal("SampleAction", result.ActionName); + Assert.Equal("SampleController", result.ControllerName); + Assert.Equal(expected, result.RouteValues); + } + + [Fact] + public void CreatedAtAction_IDisposableObject_RegistersForDispose() + { + // Arrange + var mockHttpContext = new Mock(); + mockHttpContext.Setup(x => x.Response.RegisterForDispose(It.IsAny())); + + var controller = new TestableController(); + controller.ControllerContext.HttpContext = mockHttpContext.Object; + + var input = new DisposableObject(); + + // Act + var result = controller.CreatedAtAction("SampleAction", input); + + // Assert + Assert.IsType(result); + Assert.Equal(StatusCodes.Status201Created, result.StatusCode); + Assert.Equal("SampleAction", result.ActionName); + Assert.Same(input, result.Value); + mockHttpContext.Verify( + x => x.Response.RegisterForDispose(It.IsAny()), + Times.Once()); + } + + [Fact] + public void CreatedAtRoute_WithParameterRouteName_SetsResultSameRouteName() + { + // Arrange + var controller = new TestableController(); + var routeName = "SampleRoute"; + + // Act + var result = controller.CreatedAtRoute(routeName, null); + + // Assert + Assert.IsType(result); + Assert.Same(routeName, result.RouteName); + } + + [Fact] + public void CreatedAtRoute_WithParameterRouteValues_SetsResultSameRouteValues() + { + // Arrange + var controller = new TestableController(); + var expected = new Dictionary + { + { "test", "case" }, + { "sample", "route" }, + }; + + // Act + var result = controller.CreatedAtRoute(new RouteValueDictionary(expected), null); + + // Assert + Assert.IsType(result); + Assert.Equal(StatusCodes.Status201Created, result.StatusCode); + Assert.Equal(expected, result.RouteValues); + } + + [Fact] + public void CreatedAtRoute_WithParameterRouteNameAndValues_SetsResultSameProperties() + { + // Arrange + var controller = new TestableController(); + var routeName = "SampleRoute"; + var expected = new Dictionary + { + { "test", "case" }, + { "sample", "route" }, + }; + + // Act + var result = controller.CreatedAtRoute(routeName, new RouteValueDictionary(expected), null); + + // Assert + Assert.IsType(result); + Assert.Equal(StatusCodes.Status201Created, result.StatusCode); + Assert.Same(routeName, result.RouteName); + Assert.Equal(expected, result.RouteValues); + } + + [Fact] + public void CreatedAtRoute_IDisposableObject_RegistersForDispose() + { + // Arrange + var mockHttpContext = new Mock(); + mockHttpContext.Setup(x => x.Response.RegisterForDispose(It.IsAny())); + + var controller = new TestableController(); + controller.ControllerContext.HttpContext = mockHttpContext.Object; + + var input = new DisposableObject(); + + // Act + var result = controller.CreatedAtRoute("SampleRoute", input); + + // Assert + Assert.IsType(result); + Assert.Equal(StatusCodes.Status201Created, result.StatusCode); + Assert.Equal("SampleRoute", result.RouteName); + Assert.Same(input, result.Value); + mockHttpContext.Verify( + x => x.Response.RegisterForDispose(It.IsAny()), + Times.Once()); + } + + [Fact] + public void File_WithContents() + { + // Arrange + var controller = new TestableController(); + var fileContents = new byte[0]; + + // Act + var result = controller.File(fileContents, "application/pdf"); + + // Assert + Assert.NotNull(result); + Assert.Same(fileContents, result.FileContents); + Assert.Equal("application/pdf", result.ContentType.ToString()); + Assert.Equal(string.Empty, result.FileDownloadName); + } + + [Fact] + public void File_WithContentsAndFileDownloadName() + { + // Arrange + var controller = new TestableController(); + var fileContents = new byte[0]; + + // Act + var result = controller.File(fileContents, "application/pdf", "someDownloadName"); + + // Assert + Assert.NotNull(result); + Assert.Same(fileContents, result.FileContents); + Assert.Equal("application/pdf", result.ContentType.ToString()); + Assert.Equal("someDownloadName", result.FileDownloadName); + } + + [Fact] + public void File_WithPath() + { + // Arrange + var controller = new TestableController(); + var path = Path.GetFullPath("somepath"); + + // Act + var result = controller.File(path, "application/pdf"); + + // Assert + Assert.NotNull(result); + Assert.Equal(path, result.FileName); + Assert.Equal("application/pdf", result.ContentType.ToString()); + Assert.Equal(string.Empty, result.FileDownloadName); + } + + [Fact] + public void File_WithPathAndFileDownloadName() + { + // Arrange + var controller = new TestableController(); + var path = Path.GetFullPath("somepath"); + + // Act + var result = controller.File(path, "application/pdf", "someDownloadName"); + + // Assert + Assert.NotNull(result); + Assert.Equal(path, result.FileName); + Assert.Equal("application/pdf", result.ContentType.ToString()); + Assert.Equal("someDownloadName", result.FileDownloadName); + } + + [Fact] + public void File_WithStream() + { + // Arrange + var mockHttpContext = new Mock(); + mockHttpContext.Setup(x => x.Response.RegisterForDispose(It.IsAny())); + + var controller = new TestableController(); + controller.ControllerContext.HttpContext = mockHttpContext.Object; + + var fileStream = Stream.Null; + + // Act + var result = controller.File(fileStream, "application/pdf"); + + // Assert + Assert.NotNull(result); + Assert.Same(fileStream, result.FileStream); + Assert.Equal("application/pdf", result.ContentType.ToString()); + Assert.Equal(string.Empty, result.FileDownloadName); + } + + [Fact] + public void File_WithStreamAndFileDownloadName() + { + // Arrange + var mockHttpContext = new Mock(); + mockHttpContext.Setup(x => x.Response.RegisterForDispose(It.IsAny())); + + var controller = new TestableController(); + controller.ControllerContext.HttpContext = mockHttpContext.Object; + + var fileStream = Stream.Null; + + // Act + var result = controller.File(fileStream, "application/pdf", "someDownloadName"); + + // Assert + Assert.NotNull(result); + Assert.Same(fileStream, result.FileStream); + Assert.Equal("application/pdf", result.ContentType.ToString()); + Assert.Equal("someDownloadName", result.FileDownloadName); + mockHttpContext.Verify( + x => x.Response.RegisterForDispose(It.IsAny()), + Times.Once()); + } + + [Fact] + public void HttpUnauthorized_SetsStatusCode() + { + // Arrange + var controller = new TestableController(); + + // Act + var result = controller.HttpUnauthorized(); + + // Assert + Assert.IsType(result); + Assert.Equal(StatusCodes.Status401Unauthorized, result.StatusCode); + } + + [Fact] + public void HttpNotFound_SetsStatusCode() + { + // Arrange + var controller = new TestableController(); + + // Act + var result = controller.HttpNotFound(); + + // Assert + Assert.IsType(result); + Assert.Equal(StatusCodes.Status404NotFound, result.StatusCode); + } + + [Fact] + public void HttpNotFound_SetsStatusCodeAndResponseContent() + { + // Arrange + var controller = new TestableController(); + + // Act + var result = controller.HttpNotFound("Test Content"); + + // Assert + Assert.IsType(result); + Assert.Equal(StatusCodes.Status404NotFound, result.StatusCode); + Assert.Equal("Test Content", result.Value); + } + + [Fact] + public void HttpNotFound_IDisposableObject_RegistersForDispose() + { + // Arrange + var mockHttpContext = new Mock(); + mockHttpContext.Setup(x => x.Response.RegisterForDispose(It.IsAny())); + + var controller = new TestableController(); + controller.ControllerContext.HttpContext = mockHttpContext.Object; + + var input = new DisposableObject(); + + // Act + var result = controller.HttpNotFound(input); + + // Assert + Assert.IsType(result); + Assert.Equal(StatusCodes.Status404NotFound, result.StatusCode); + Assert.Same(input, result.Value); + mockHttpContext.Verify( + x => x.Response.RegisterForDispose(It.IsAny()), + Times.Once()); + } + + [Fact] + public void Ok_SetsStatusCode() + { + // Arrange + var controller = new TestableController(); + + // Act + var result = controller.Ok(); + + // Assert + Assert.IsType(result); + Assert.Equal(StatusCodes.Status200OK, result.StatusCode); + } + + [Fact] + public void Ok_WithIDisposableObject_RegistersForDispose() + { + // Arrange + var mockHttpContext = new Mock(); + mockHttpContext.Setup(x => x.Response.RegisterForDispose(It.IsAny())); + + var controller = new TestableController(); + controller.ControllerContext.HttpContext = mockHttpContext.Object; + + var input = new DisposableObject(); + + // Act + var result = controller.Ok(input); + + // Assert + Assert.IsType(result); + Assert.Equal(StatusCodes.Status200OK, result.StatusCode); + Assert.Same(input, result.Value); + mockHttpContext.Verify( + x => x.Response.RegisterForDispose(It.IsAny()), + Times.Once()); + } + + [Fact] + public void BadRequest_SetsStatusCode() + { + // Arrange + var controller = new TestableController(); + + // Act + var result = controller.HttpBadRequest(); + + // Assert + Assert.IsType(result); + Assert.Equal(StatusCodes.Status400BadRequest, result.StatusCode); + } + + [Fact] + public void BadRequest_SetsStatusCodeAndValue_Object() + { + // Arrange + var controller = new TestableController(); + var obj = new object(); + + // Act + var result = controller.HttpBadRequest(obj); + + // Assert + Assert.IsType(result); + Assert.Equal(StatusCodes.Status400BadRequest, result.StatusCode); + Assert.Equal(obj, result.Value); + } + + [Fact] + public void BadRequest_IDisposableObject_RegistersForDispose() + { + // Arrange + var mockHttpContext = new Mock(); + mockHttpContext.Setup(x => x.Response.RegisterForDispose(It.IsAny())); + + var controller = new TestableController(); + controller.ControllerContext.HttpContext = mockHttpContext.Object; + + var input = new DisposableObject(); + + // Act + var result = controller.HttpBadRequest(input); + + // Assert + Assert.IsType(result); + Assert.Equal(StatusCodes.Status400BadRequest, result.StatusCode); + Assert.Same(input, result.Value); + mockHttpContext.Verify( + x => x.Response.RegisterForDispose(It.IsAny()), + Times.Once()); + } + + [Fact] + public void BadRequest_SetsStatusCodeAndValue_ModelState() + { + // Arrange + var controller = new TestableController(); + + // Act + var result = controller.HttpBadRequest(new ModelStateDictionary()); + + // Assert + Assert.IsType(result); + Assert.Equal(StatusCodes.Status400BadRequest, result.StatusCode); + var errors = Assert.IsType(result.Value); + Assert.Equal(0, errors.Count); + } + + [Theory] + [MemberData(nameof(PublicNormalMethodsFromControllerBase))] + public void NonActionAttribute_IsOnEveryPublicNormalMethodFromControllerBase(MethodInfo method) + { + // Arrange & Act & Assert + Assert.True(method.IsDefined(typeof(NonActionAttribute))); + } + + [Fact] + public void Controller_NoContent() + { + // Arrange + var controller = new TestableController(); + + // Act + var result = controller.NoContent(); + + // Assert + Assert.Equal(StatusCodes.Status204NoContent, result.StatusCode); + } + + [Fact] + public void Controller_Content_WithParameterContentString_SetsResultContent() + { + // Arrange + var controller = new TestableController(); + + // Act + var actualContentResult = controller.Content("TestContent"); + + // Assert + Assert.IsType(actualContentResult); + Assert.Equal("TestContent", actualContentResult.Content); + Assert.Null(actualContentResult.ContentType); + } + + [Fact] + public void Controller_Content_WithParameterContentStringAndContentType_SetsResultContentAndContentType() + { + // Arrange + var controller = new TestableController(); + + // Act + var actualContentResult = controller.Content("TestContent", "text/plain"); + + // Assert + Assert.IsType(actualContentResult); + Assert.Equal("TestContent", actualContentResult.Content); + Assert.Null(actualContentResult.ContentType.Encoding); + Assert.Equal("text/plain", actualContentResult.ContentType.ToString()); + } + + [Fact] + public void Controller_Content_WithParameterContentAndTypeAndEncoding_SetsResultContentAndTypeAndEncoding() + { + // Arrange + var controller = new TestableController(); + + // Act + var actualContentResult = controller.Content("TestContent", "text/plain", Encoding.UTF8); + + // Assert + Assert.IsType(actualContentResult); + Assert.Equal("TestContent", actualContentResult.Content); + Assert.Same(Encoding.UTF8, actualContentResult.ContentType.Encoding); + Assert.Equal("text/plain; charset=utf-8", actualContentResult.ContentType.ToString()); + } + + [Fact] + public void Controller_Content_NoContentType_DefaultEncodingIsUsed() + { + // Arrange + var contentController = new ContentController(); + var expected = MediaTypeHeaderValue.Parse("text/plain; charset=utf-8"); + + // Act + var contentResult = (ContentResult)contentController.Content_WithNoEncoding(); + + // Assert + // The default content type of ContentResult is used when the result is executed. + Assert.Null(contentResult.ContentType); + } + + [Fact] + public void Controller_Content_InvalidCharset_DefaultEncodingIsUsed() + { + // Arrange + var contentController = new ContentController(); + var contentType = "text/xml; charset=invalid; p1=p1-value"; + + // Act + var contentResult = (ContentResult)contentController.Content_WithInvalidCharset(); + + // Assert + Assert.NotNull(contentResult.ContentType); + Assert.Equal(contentType, contentResult.ContentType.ToString()); + // The default encoding of ContentResult is used when this result is executed. + Assert.Null(contentResult.ContentType.Encoding); + } + + [Fact] + public void Controller_Content_CharsetAndEncodingProvided_EncodingIsUsed() + { + // Arrange + var contentController = new ContentController(); + var contentType = MediaTypeHeaderValue.Parse("text/xml; charset=us-ascii; p1=p1-value"); + + // Act + var contentResult = (ContentResult)contentController.Content_WithEncodingInCharset_AndEncodingParameter(); + + // Assert + Assert.Equal(contentType, contentResult.ContentType); + } + + [Fact] + public void Controller_Content_CharsetInContentType_IsUsedForEncoding() + { + // Arrange + var contentController = new ContentController(); + var contentType = MediaTypeHeaderValue.Parse("text/xml; charset=us-ascii; p1=p1-value"); + + // Act + var contentResult = (ContentResult)contentController.Content_WithEncodingInCharset(); + + // Assert + Assert.Equal(contentType, contentResult.ContentType); + } + + public static IEnumerable RedirectTestData + { + get + { + yield return new object[] + { + null, + null, + }; + + yield return new object[] + { + new Dictionary { { "hello", "world" } }, + new RouteValueDictionary() { { "hello", "world" } }, + }; + + var expected2 = new Dictionary + { + { "test", "case" }, + { "sample", "route" }, + }; + + yield return new object[] + { + new RouteValueDictionary(expected2), + new RouteValueDictionary(expected2), + }; + } + } + + [Fact] + public async Task TryUpdateModel_FallsBackOnEmptyPrefix_IfNotSpecified() + { + // Arrange + var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); + var valueProvider = Mock.Of(); + var binder = new Mock(); + binder.Setup(b => b.BindModelAsync(It.IsAny())) + .Callback((ModelBindingContext context) => + { + Assert.Empty(context.ModelName); + Assert.Same(valueProvider, Assert.IsType(context.ValueProvider)[0]); + + // Include and exclude should be null, resulting in property + // being included. + Assert.True(context.PropertyFilter(context, "Property1")); + Assert.True(context.PropertyFilter(context, "Property2")); + }) + .Returns(ModelBindingResult.NoResultAsync) + .Verifiable(); + + var controller = GetController(binder.Object, valueProvider); + var model = new MyModel(); + + // Act + var result = await controller.TryUpdateModelAsync(model); + + // Assert + binder.Verify(); + } + + [Fact] + public async Task TryUpdateModel_UsesModelTypeNameIfSpecified() + { + // Arrange + var modelName = "mymodel"; + + var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); + var valueProvider = Mock.Of(); + var binder = new Mock(); + binder.Setup(b => b.BindModelAsync(It.IsAny())) + .Callback((ModelBindingContext context) => + { + Assert.Same(valueProvider, Assert.IsType(context.ValueProvider)[0]); + + // Include and exclude should be null, resulting in property + // being included. + Assert.True(context.PropertyFilter(context, "Property1")); + Assert.True(context.PropertyFilter(context, "Property2")); + }) + .Returns(ModelBindingResult.NoResultAsync) + .Verifiable(); + + var controller = GetController(binder.Object, valueProvider); + var model = new MyModel(); + + // Act + var result = await controller.TryUpdateModelAsync(model, modelName); + + // Assert + binder.Verify(); + } + + [Fact] + public async Task TryUpdateModel_UsesModelValueProviderIfSpecified() + { + // Arrange + var modelName = "mymodel"; + + var valueProvider = Mock.Of(); + var binder = new Mock(); + binder.Setup(b => b.BindModelAsync(It.IsAny())) + .Callback((ModelBindingContext context) => + { + Assert.Same(valueProvider, context.ValueProvider); + + // Include and exclude should be null, resulting in property + // being included. + Assert.True(context.PropertyFilter(context, "Property1")); + Assert.True(context.PropertyFilter(context, "Property2")); + }) + .Returns(ModelBindingResult.NoResultAsync) + .Verifiable(); + + var controller = GetController(binder.Object, valueProvider: null); + var model = new MyModel(); + + // Act + var result = await controller.TryUpdateModelAsync(model, modelName, valueProvider); + + // Assert + binder.Verify(); + } + + [Fact] + public async Task TryUpdateModel_PredicateOverload_UsesPassedArguments() + { + // Arrange + var modelName = "mymodel"; + + Func includePredicate = (context, propertyName) => + string.Equals(propertyName, "include1", StringComparison.OrdinalIgnoreCase) || + string.Equals(propertyName, "include2", StringComparison.OrdinalIgnoreCase); + + var binder = new Mock(); + var valueProvider = Mock.Of(); + binder.Setup(b => b.BindModelAsync(It.IsAny())) + .Callback((ModelBindingContext context) => + { + Assert.Same(valueProvider, Assert.IsType(context.ValueProvider)[0]); + + Assert.True(context.PropertyFilter(context, "include1")); + Assert.True(context.PropertyFilter(context, "include2")); + + Assert.False(context.PropertyFilter(context, "exclude1")); + Assert.False(context.PropertyFilter(context, "exclude2")); + }) + .Returns(ModelBindingResult.NoResultAsync) + .Verifiable(); + + var controller = GetController(binder.Object, valueProvider); + var model = new MyModel(); + + // Act + await controller.TryUpdateModelAsync(model, modelName, includePredicate); + + // Assert + binder.Verify(); + } + + [Fact] + public async Task TryUpdateModel_PredicateWithValueProviderOverload_UsesPassedArguments() + { + // Arrange + var modelName = "mymodel"; + + Func includePredicate = + (context, propertyName) => string.Equals(propertyName, "include1", StringComparison.OrdinalIgnoreCase) || + string.Equals(propertyName, "include2", StringComparison.OrdinalIgnoreCase); + + var binder = new Mock(); + var valueProvider = Mock.Of(); + binder.Setup(b => b.BindModelAsync(It.IsAny())) + .Callback((ModelBindingContext context) => + { + Assert.Same(valueProvider, context.ValueProvider); + + Assert.True(context.PropertyFilter(context, "include1")); + Assert.True(context.PropertyFilter(context, "include2")); + + Assert.False(context.PropertyFilter(context, "exclude1")); + Assert.False(context.PropertyFilter(context, "exclude2")); + }) + .Returns(ModelBindingResult.NoResultAsync) + .Verifiable(); + + var controller = GetController(binder.Object, valueProvider: null); + + var model = new MyModel(); + + // Act + await controller.TryUpdateModelAsync(model, modelName, valueProvider, includePredicate); + + // Assert + binder.Verify(); + } + + [Theory] + [InlineData("")] + [InlineData("prefix")] + public async Task TryUpdateModel_IncludeExpressionOverload_UsesPassedArguments(string prefix) + { + // Arrange + var valueProvider = new Mock(); + valueProvider + .Setup(v => v.ContainsPrefix(prefix)) + .Returns(true); + + var binder = new Mock(); + binder.Setup(b => b.BindModelAsync(It.IsAny())) + .Callback((ModelBindingContext context) => + { + Assert.Same( + valueProvider.Object, + Assert.IsType(context.ValueProvider)[0]); + + Assert.True(context.PropertyFilter(context, "Property1")); + Assert.True(context.PropertyFilter(context, "Property2")); + + Assert.False(context.PropertyFilter(context, "exclude1")); + Assert.False(context.PropertyFilter(context, "exclude2")); + }) + .Returns(ModelBindingResult.NoResultAsync) + .Verifiable(); + + + var controller = GetController(binder.Object, valueProvider.Object); + var model = new MyModel(); + + // Act + await controller.TryUpdateModelAsync(model, prefix, m => m.Property1, m => m.Property2); + + // Assert + binder.Verify(); + } + + [Theory] + [InlineData("")] + [InlineData("prefix")] + public async Task + TryUpdateModel_IncludeExpressionWithValueProviderOverload_UsesPassedArguments(string prefix) + { + // Arrange + var valueProvider = new Mock(); + valueProvider + .Setup(v => v.ContainsPrefix(prefix)) + .Returns(true); + + var binder = new Mock(); + binder.Setup(b => b.BindModelAsync(It.IsAny())) + .Callback((ModelBindingContext context) => + { + Assert.Same(valueProvider.Object, context.ValueProvider); + + Assert.True(context.PropertyFilter(context, "Property1")); + Assert.True(context.PropertyFilter(context, "Property2")); + + Assert.False(context.PropertyFilter(context, "exclude1")); + Assert.False(context.PropertyFilter(context, "exclude2")); + }) + .Returns(ModelBindingResult.NoResultAsync) + .Verifiable(); + + var controller = GetController(binder.Object, valueProvider: null); + var model = new MyModel(); + + // Act + await controller.TryUpdateModelAsync(model, prefix, valueProvider.Object, m => m.Property1, m => m.Property2); + + // Assert + binder.Verify(); + } + + [Fact] + public async Task TryUpdateModelNonGeneric_PredicateWithValueProviderOverload_UsesPassedArguments() + { + // Arrange + var modelName = "mymodel"; + + Func includePredicate = + (context, propertyName) => string.Equals(propertyName, "include1", StringComparison.OrdinalIgnoreCase) || + string.Equals(propertyName, "include2", StringComparison.OrdinalIgnoreCase); + + var valueProvider = Mock.Of(); + + var binder = new Mock(); + binder.Setup(b => b.BindModelAsync(It.IsAny())) + .Callback((ModelBindingContext context) => + { + Assert.Same(valueProvider, context.ValueProvider); + + Assert.True(context.PropertyFilter(context, "include1")); + Assert.True(context.PropertyFilter(context, "include2")); + + Assert.False(context.PropertyFilter(context, "exclude1")); + Assert.False(context.PropertyFilter(context, "exclude2")); + }) + .Returns(ModelBindingResult.NoResultAsync) + .Verifiable(); + + var controller = GetController(binder.Object, valueProvider: null); + + var model = new MyModel(); + + // Act + await controller.TryUpdateModelAsync(model, model.GetType(), modelName, valueProvider, includePredicate); + + // Assert + binder.Verify(); + } + + [Fact] + public async Task TryUpdateModelNonGeneric_ModelTypeOverload_UsesPassedArguments() + { + // Arrange + var modelName = "mymodel"; + + var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); + var valueProvider = Mock.Of(); + var binder = new Mock(); + binder.Setup(b => b.BindModelAsync(It.IsAny())) + .Callback((ModelBindingContext context) => + { + Assert.Same(valueProvider, Assert.IsType(context.ValueProvider)[0]); + + // Include and exclude should be null, resulting in property + // being included. + Assert.True(context.PropertyFilter(context, "Property1")); + Assert.True(context.PropertyFilter(context, "Property2")); + }) + .Returns(ModelBindingResult.NoResultAsync) + .Verifiable(); + + var controller = GetController(binder.Object, valueProvider); + var model = new MyModel(); + + // Act + var result = await controller.TryUpdateModelAsync(model, model.GetType(), modelName); + + // Assert + binder.Verify(); + } + + [Fact] + public async Task TryUpdateModelNonGeneric_BindToBaseDeclaredType_ModelTypeOverload() + { + // Arrange + var modelName = "mymodel"; + + var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); + var valueProvider = Mock.Of(); + var binder = new Mock(); + binder.Setup(b => b.BindModelAsync(It.IsAny())) + .Callback((ModelBindingContext context) => + { + Assert.Same(valueProvider, Assert.IsType(context.ValueProvider)[0]); + + // Include and exclude should be null, resulting in property + // being included. + Assert.True(context.PropertyFilter(context, "Property1")); + Assert.True(context.PropertyFilter(context, "Property2")); + }) + .Returns(ModelBindingResult.NoResultAsync) + .Verifiable(); + + var controller = GetController(binder.Object, valueProvider); + MyModel model = new MyDerivedModel(); + + // Act + var result = await controller.TryUpdateModelAsync(model, model.GetType(), modelName); + + // Assert + binder.Verify(); + } + + [Fact] + public void ControllerExposes_RequestServices() + { + // Arrange + var controller = new TestableController(); + + var serviceProvider = Mock.Of(); + var httpContext = new Mock(); + httpContext.Setup(c => c.RequestServices) + .Returns(serviceProvider); + + controller.ControllerContext.HttpContext = httpContext.Object; + + // Act + var innerServiceProvider = controller.Resolver; + + // Assert + Assert.Same(serviceProvider, innerServiceProvider); + } + + [Fact] + public void ControllerExposes_Request() + { + // Arrange + var controller = new TestableController(); + + var request = Mock.Of(); + var httpContext = new Mock(); + httpContext.Setup(c => c.Request) + .Returns(request); + + controller.ControllerContext.HttpContext = httpContext.Object; + + // Act + var innerRequest = controller.Request; + + // Assert + Assert.Same(request, innerRequest); + } + + [Fact] + public void ControllerExposes_Response() + { + // Arrange + var controller = new TestableController(); + + var response = Mock.Of(); + var httpContext = new Mock(); + httpContext.Setup(c => c.Response) + .Returns(response); + + controller.ControllerContext.HttpContext = httpContext.Object; + + // Act + var innerResponse = controller.Response; + + // Assert + Assert.Same(response, innerResponse); + } + + [Fact] + public void ControllerExposes_RouteData() + { + // Arrange + var controller = new TestableController(); + + var routeData = Mock.Of(); + controller.ControllerContext.RouteData = routeData; + + // Act + var innerRouteData = controller.RouteData; + + // Assert + Assert.Same(routeData, innerRouteData); + } + + [Fact] + public void TryValidateModelWithValidModel_ReturnsTrue() + { + // Arrange + var binder = new Mock(); + var controller = GetController(binder.Object, valueProvider: null); + controller.ControllerContext.ValidatorProviders = new List() + { + Mock.Of(), + }; + + var model = new TryValidateModelModel(); + + // Act + var result = controller.TryValidateModel(model); + + // Assert + Assert.True(result); + Assert.True(controller.ModelState.IsValid); + } + + [Fact] + public void TryValidateModelWithInvalidModelWithPrefix_ReturnsFalse() + { + // Arrange + var model = new TryValidateModelModel(); + var validationResult = new[] + { + new ModelValidationResult(string.Empty, "Out of range!") + }; + + var validator1 = new Mock(); + + validator1.Setup(v => v.Validate(It.IsAny())) + .Returns(validationResult); + + var provider = new Mock(); + provider.Setup(v => v.GetValidators(It.IsAny())) + .Callback(c => c.Validators.Add(validator1.Object)); + + var binder = new Mock(); + var controller = GetController(binder.Object, valueProvider: null); + controller.ControllerContext.ValidatorProviders = new List() + { + provider.Object, + }; + + // Act + var result = controller.TryValidateModel(model, "Prefix"); + + // Assert + Assert.False(result); + Assert.Equal(1, controller.ModelState.Count); + var error = Assert.Single(controller.ModelState["Prefix.IntegerProperty"].Errors); + Assert.Equal("Out of range!", error.ErrorMessage); + } + + [Fact] + public void TryValidateModelWithInvalidModelNoPrefix_ReturnsFalse() + { + // Arrange + var model = new TryValidateModelModel(); + var validationResult = new[] + { + new ModelValidationResult(string.Empty, "Out of range!") + }; + + var validator1 = new Mock(); + + validator1.Setup(v => v.Validate(It.IsAny())) + .Returns(validationResult); + + var provider = new Mock(); + provider.Setup(v => v.GetValidators(It.IsAny())) + .Callback(c => c.Validators.Add(validator1.Object)); + + var binder = new Mock(); + var controller = GetController(binder.Object, valueProvider: null); + controller.ControllerContext.ValidatorProviders = new List() + { + provider.Object, + }; + + // Act + var result = controller.TryValidateModel(model); + + // Assert + Assert.False(result); + Assert.Equal(1, controller.ModelState.Count); + var error = Assert.Single(controller.ModelState["IntegerProperty"].Errors); + Assert.Equal("Out of range!", error.ErrorMessage); + } + + [Fact] + public void TryValidateModel_Succeeds_WithoutValidatorMetadata() + { + // Arrange + // Do not add a Mock validator provider to this test. Test is intended to demonstrate ease of unit testing + // and exercise DataAnnotationsModelValidatorProvider, avoiding #3586 regressions. + var model = new TryValidateModelModel(); + var controller = GetController(binder: null, valueProvider: null); + + // Act + var result = controller.TryValidateModel(model); + + // Assert + Assert.True(controller.ModelState.IsValid); + } + + private static ControllerBase GetController(IModelBinder binder, IValueProvider valueProvider) + { + var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); + var httpContext = new DefaultHttpContext(); + + var controllerContext = new ControllerContext() + { + HttpContext = httpContext, + ModelBinders = new[] { binder, }, + ValueProviders = new[] { valueProvider, }, + ValidatorProviders = new[] + { + new DataAnnotationsModelValidatorProvider( + new ValidationAttributeAdapterProvider(), + new TestOptionsManager(), + stringLocalizerFactory: null), + }, + }; + + var controller = new TestableController() + { + ControllerContext = controllerContext, + MetadataProvider = metadataProvider, + ObjectValidator = new DefaultObjectValidator(metadataProvider), + }; + + return controller; + } + + private class MyModel + { + public string Property1 { get; set; } + public string Property2 { get; set; } + } + + private class MyDerivedModel : MyModel + { + public string Property3 { get; set; } + } + + private class TryValidateModelModel + { + public int IntegerProperty { get; set; } + } + + + private class TestableController : ControllerBase + { + } + + private class DisposableObject : IDisposable + { + public void Dispose() + { + throw new NotImplementedException(); + } + } + + private class ContentController : ControllerBase + { + public IActionResult Content_WithNoEncoding() + { + return Content("Hello!!"); + } + + public IActionResult Content_WithEncodingInCharset() + { + return Content("Hello!!", "text/xml; charset=us-ascii; p1=p1-value"); + } + + public IActionResult Content_WithInvalidCharset() + { + return Content("Hello!!", "text/xml; charset=invalid; p1=p1-value"); + } + + public IActionResult Content_WithEncodingInCharset_AndEncodingParameter() + { + return Content("Hello!!", "text/xml; charset=invalid; p1=p1-value", Encoding.ASCII); + } + } + } +} diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Controllers/DefaultControllerTypeProviderTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/Controllers/DefaultControllerTypeProviderTest.cs index 86a63648b9..17d4303490 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/Controllers/DefaultControllerTypeProviderTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/Controllers/DefaultControllerTypeProviderTest.cs @@ -18,7 +18,7 @@ namespace Microsoft.AspNet.Mvc.Controllers }; [Fact] - public void IsController_UserDefinedClass() + public void IsController_UserDefinedClass_DerivedFromController() { // Arrange var typeInfo = typeof(StoreController).GetTypeInfo(); @@ -31,6 +31,34 @@ namespace Microsoft.AspNet.Mvc.Controllers Assert.True(isController); } + [Fact] + public void IsController_UserDefinedClass_DerivedFromControllerBase() + { + // Arrange + var typeInfo = typeof(ProductsController).GetTypeInfo(); + var provider = GetControllerTypeProvider(); + + // Act + var isController = provider.IsController(typeInfo, CandidateAssemblies); + + // Assert + Assert.True(isController); + } + + [Fact] + public void IsController_UserDefinedClass_DerivedFromControllerBase_WithoutSuffix() + { + // Arrange + var typeInfo = typeof(Products).GetTypeInfo(); + var provider = GetControllerTypeProvider(); + + // Act + var isController = provider.IsController(typeInfo, CandidateAssemblies); + + // Assert + Assert.False(isController); + } + [Fact] public void IsController_FrameworkControllerClass() { @@ -45,6 +73,20 @@ namespace Microsoft.AspNet.Mvc.Controllers Assert.False(isController); } + [Fact] + public void IsController_FrameworkBaseControllerClass() + { + // Arrange + var typeInfo = typeof(Controller).GetTypeInfo(); + var provider = GetControllerTypeProvider(); + + // Act + var isController = provider.IsController(typeInfo, CandidateAssemblies); + + // Assert + Assert.False(isController); + } + [Fact] public void IsController_UserDefinedControllerClass() { @@ -247,6 +289,15 @@ namespace Microsoft.AspNet.Mvc.DefaultControllerTypeProviderControllers { } + public class ProductsController : ControllerBase + { + } + + public class Products : ControllerBase + { + } + + public abstract class Controller { } diff --git a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/ControllerTest.cs b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/ControllerTest.cs index 969c004a77..69ab1d3678 100644 --- a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/ControllerTest.cs +++ b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/ControllerTest.cs @@ -11,7 +11,6 @@ using System.Threading.Tasks; using Microsoft.AspNet.Http; using Microsoft.AspNet.Http.Internal; using Microsoft.AspNet.Mvc.Filters; -using Microsoft.AspNet.Mvc.Formatters; using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.AspNet.Mvc.ModelBinding.Validation; using Microsoft.AspNet.Mvc.ViewFeatures; @@ -61,898 +60,6 @@ namespace Microsoft.AspNet.Mvc.Test Assert.Equal("property", controller.ViewData["Another"]); } - [Fact] - public void Redirect_WithParameterUrl_SetsRedirectResultSameUrl() - { - // Arrange - var controller = new TestableController(); - var url = "/test/url"; - - // Act - var result = controller.Redirect(url); - - // Assert - Assert.IsType(result); - Assert.False(result.Permanent); - Assert.Same(url, result.Url); - } - - [Fact] - public void RedirectPermanent_WithParameterUrl_SetsRedirectResultPermanentAndSameUrl() - { - // Arrange - var controller = new TestableController(); - var url = "/test/url"; - - // Act - var result = controller.RedirectPermanent(url); - - // Assert - Assert.IsType(result); - Assert.True(result.Permanent); - Assert.Same(url, result.Url); - } - - [Theory] - [InlineData(null)] - [InlineData("")] - public void Redirect_WithParameter_NullOrEmptyUrl_Throws(string url) - { - // Arrange - var controller = new TestableController(); - - // Act & Assert - ExceptionAssert.ThrowsArgumentNullOrEmpty( - () => controller.Redirect(url: url), "url"); - } - - [Fact] - public void LocalRedirect_WithParameterUrl_SetsLocalRedirectResultWithSameUrl() - { - // Arrange - var controller = new TestableController(); - var url = "/test/url"; - - // Act - var result = controller.LocalRedirect(url); - - // Assert - Assert.IsType(result); - Assert.False(result.Permanent); - Assert.Same(url, result.Url); - } - - [Fact] - public void LocalRedirectPermanent_WithParameterUrl_SetsLocalRedirectResultPermanentWithSameUrl() - { - // Arrange - var controller = new TestableController(); - var url = "/test/url"; - - // Act - var result = controller.LocalRedirectPermanent(url); - - // Assert - Assert.IsType(result); - Assert.True(result.Permanent); - Assert.Same(url, result.Url); - } - - [Theory] - [InlineData(null)] - [InlineData("")] - public void LocalRedirect_WithParameter_NullOrEmptyUrl_Throws(string url) - { - // Arrange - var controller = new TestableController(); - - // Act & Assert - ExceptionAssert.ThrowsArgumentNullOrEmpty( - () => controller.LocalRedirect(localUrl: url), "localUrl"); - } - - [Theory] - [InlineData(null)] - [InlineData("")] - public void RedirectPermanent_WithParameter_NullOrEmptyUrl_Throws(string url) - { - // Arrange - var controller = new TestableController(); - - // Act & Assert - ExceptionAssert.ThrowsArgumentNullOrEmpty( - () => controller.RedirectPermanent(url: url), "url"); - } - - [Fact] - public void RedirectToAction_WithParameterActionName_SetsResultActionName() - { - // Arrange - var controller = new TestableController(); - - // Act - var resultTemporary = controller.RedirectToAction("SampleAction"); - - // Assert - Assert.IsType(resultTemporary); - Assert.False(resultTemporary.Permanent); - Assert.Equal("SampleAction", resultTemporary.ActionName); - } - - [Fact] - public void RedirectToActionPermanent_WithParameterActionName_SetsResultActionNameAndPermanent() - { - // Arrange - var controller = new TestableController(); - - // Act - var resultPermanent = controller.RedirectToActionPermanent("SampleAction"); - - // Assert - Assert.IsType(resultPermanent); - Assert.True(resultPermanent.Permanent); - Assert.Equal("SampleAction", resultPermanent.ActionName); - } - - [Theory] - [InlineData("")] - [InlineData(null)] - [InlineData("SampleController")] - public void RedirectToAction_WithParameterActionAndControllerName_SetsEqualNames(string controllerName) - { - // Arrange - var controller = new TestableController(); - - // Act - var resultTemporary = controller.RedirectToAction("SampleAction", controllerName); - - // Assert - Assert.IsType(resultTemporary); - Assert.False(resultTemporary.Permanent); - Assert.Equal("SampleAction", resultTemporary.ActionName); - Assert.Equal(controllerName, resultTemporary.ControllerName); - } - - [Theory] - [InlineData("")] - [InlineData(null)] - [InlineData("SampleController")] - public void RedirectToActionPermanent_WithParameterActionAndControllerName_SetsEqualNames( - string controllerName) - { - // Arrange - var controller = new TestableController(); - - // Act - var resultPermanent = controller.RedirectToActionPermanent("SampleAction", controllerName); - - // Assert - Assert.IsType(resultPermanent); - Assert.True(resultPermanent.Permanent); - Assert.Equal("SampleAction", resultPermanent.ActionName); - Assert.Equal(controllerName, resultPermanent.ControllerName); - } - - [Theory] - [MemberData(nameof(RedirectTestData))] - public void RedirectToAction_WithParameterActionControllerRouteValues_SetsResultProperties( - object routeValues, - IEnumerable> expected) - { - // Arrange - var controller = new TestableController(); - - // Act - var resultTemporary = controller.RedirectToAction("SampleAction", "SampleController", routeValues); - - // Assert - Assert.IsType(resultTemporary); - Assert.False(resultTemporary.Permanent); - Assert.Equal("SampleAction", resultTemporary.ActionName); - Assert.Equal("SampleController", resultTemporary.ControllerName); - Assert.Equal(expected, resultTemporary.RouteValues); - } - - [Theory] - [MemberData(nameof(RedirectTestData))] - public void RedirectToActionPermanent_WithParameterActionControllerRouteValues_SetsResultProperties( - object routeValues, - IEnumerable> expected) - { - // Arrange - var controller = new TestableController(); - - // Act - var resultPermanent = controller.RedirectToActionPermanent( - "SampleAction", - "SampleController", - routeValues); - - // Assert - Assert.IsType(resultPermanent); - Assert.True(resultPermanent.Permanent); - Assert.Equal("SampleAction", resultPermanent.ActionName); - Assert.Equal("SampleController", resultPermanent.ControllerName); - Assert.Equal(expected, resultPermanent.RouteValues); - } - - [Theory] - [MemberData(nameof(RedirectTestData))] - public void RedirectToAction_WithParameterActionAndRouteValues_SetsResultProperties( - object routeValues, - IEnumerable> expected) - { - // Arrange - var controller = new TestableController(); - - // Act - var resultTemporary = controller.RedirectToAction(actionName: null, routeValues: routeValues); - - // Assert - Assert.IsType(resultTemporary); - Assert.False(resultTemporary.Permanent); - Assert.Null(resultTemporary.ActionName); - Assert.Equal(expected, resultTemporary.RouteValues); - } - - [Theory] - [MemberData(nameof(RedirectTestData))] - public void RedirectToActionPermanent_WithParameterActionAndRouteValues_SetsResultProperties( - object routeValues, - IEnumerable> expected) - { - // Arrange - var controller = new TestableController(); - - // Act - var resultPermanent = controller.RedirectToActionPermanent(null, routeValues); - - // Assert - Assert.IsType(resultPermanent); - Assert.True(resultPermanent.Permanent); - Assert.Null(resultPermanent.ActionName); - Assert.Equal(expected, resultPermanent.RouteValues); - } - - [Theory] - [MemberData(nameof(RedirectTestData))] - public void RedirectToRoute_WithParameterRouteValues_SetsResultEqualRouteValues( - object routeValues, - IEnumerable> expected) - { - // Arrange - var controller = new TestableController(); - - // Act - var resultTemporary = controller.RedirectToRoute(routeValues); - - // Assert - Assert.IsType(resultTemporary); - Assert.False(resultTemporary.Permanent); - Assert.Equal(expected, resultTemporary.RouteValues); - } - - [Theory] - [MemberData(nameof(RedirectTestData))] - public void RedirectToRoutePermanent_WithParameterRouteValues_SetsResultEqualRouteValuesAndPermanent( - object routeValues, - IEnumerable> expected) - { - // Arrange - var controller = new TestableController(); - - // Act - var resultPermanent = controller.RedirectToRoutePermanent(routeValues); - - // Assert - Assert.IsType(resultPermanent); - Assert.True(resultPermanent.Permanent); - Assert.Equal(expected, resultPermanent.RouteValues); - } - - [Fact] - public void RedirectToRoute_WithParameterRouteName_SetsResultSameRouteName() - { - // Arrange - var controller = new TestableController(); - var routeName = "CustomRouteName"; - - // Act - var resultTemporary = controller.RedirectToRoute(routeName); - - // Assert - Assert.IsType(resultTemporary); - Assert.False(resultTemporary.Permanent); - Assert.Same(routeName, resultTemporary.RouteName); - } - - [Fact] - public void RedirectToRoutePermanent_WithParameterRouteName_SetsResultSameRouteNameAndPermanent() - { - // Arrange - var controller = new TestableController(); - var routeName = "CustomRouteName"; - - // Act - var resultPermanent = controller.RedirectToRoutePermanent(routeName); - - // Assert - Assert.IsType(resultPermanent); - Assert.True(resultPermanent.Permanent); - Assert.Same(routeName, resultPermanent.RouteName); - } - - [Theory] - [MemberData(nameof(RedirectTestData))] - public void RedirectToRoute_WithParameterRouteNameAndRouteValues_SetsResultSameRouteNameAndRouteValues( - object routeValues, - IEnumerable> expected) - { - // Arrange - var controller = new TestableController(); - var routeName = "CustomRouteName"; - - // Act - var resultTemporary = controller.RedirectToRoute(routeName, routeValues); - - // Assert - Assert.IsType(resultTemporary); - Assert.False(resultTemporary.Permanent); - Assert.Same(routeName, resultTemporary.RouteName); - Assert.Equal(expected, resultTemporary.RouteValues); - } - - [Theory] - [MemberData(nameof(RedirectTestData))] - public void RedirectToRoutePermanent_WithParameterRouteNameAndRouteValues_SetsResultProperties( - object routeValues, - IEnumerable> expected) - { - // Arrange - var controller = new TestableController(); - var routeName = "CustomRouteName"; - - // Act - var resultPermanent = controller.RedirectToRoutePermanent(routeName, routeValues); - - // Assert - Assert.IsType(resultPermanent); - Assert.True(resultPermanent.Permanent); - Assert.Same(routeName, resultPermanent.RouteName); - Assert.Equal(expected, resultPermanent.RouteValues); - } - - [Fact] - public void Created_WithStringParameter_SetsCreatedLocation() - { - // Arrange - var controller = new TestableController(); - var uri = "http://test/url"; - - // Act - var result = controller.Created(uri, null); - - // Assert - Assert.IsType(result); - Assert.Equal(StatusCodes.Status201Created, result.StatusCode); - Assert.Same(uri, result.Location); - } - - [Fact] - public void Created_WithAbsoluteUriParameter_SetsCreatedLocation() - { - // Arrange - var controller = new TestableController(); - var uri = new Uri("http://test/url"); - - // Act - var result = controller.Created(uri, null); - - // Assert - Assert.IsType(result); - Assert.Equal(StatusCodes.Status201Created, result.StatusCode); - Assert.Equal(uri.OriginalString, result.Location); - } - - [Fact] - public void Created_WithRelativeUriParameter_SetsCreatedLocation() - { - // Arrange - var controller = new TestableController(); - var uri = new Uri("/test/url", UriKind.Relative); - - // Act - var result = controller.Created(uri, null); - - // Assert - Assert.IsType(result); - Assert.Equal(StatusCodes.Status201Created, result.StatusCode); - Assert.Equal(uri.OriginalString, result.Location); - } - - [Fact] - public void Created_IDisposableObject_RegistersForDispose() - { - // Arrange - var mockHttpContext = new Mock(); - mockHttpContext.Setup(x => x.Response.RegisterForDispose(It.IsAny())); - var uri = new Uri("/test/url", UriKind.Relative); - - var controller = new TestableController(); - controller.ControllerContext.HttpContext = mockHttpContext.Object; - - var input = new DisposableObject(); - - // Act - var result = controller.Created(uri, input); - - // Assert - Assert.IsType(result); - Assert.Equal(StatusCodes.Status201Created, result.StatusCode); - Assert.Equal(uri.OriginalString, result.Location); - Assert.Same(input, result.Value); - mockHttpContext.Verify( - x => x.Response.RegisterForDispose(It.IsAny()), - Times.Once()); - } - - [Fact] - public void CreatedAtAction_WithParameterActionName_SetsResultActionName() - { - // Arrange - var controller = new TestableController(); - - // Act - var result = controller.CreatedAtAction("SampleAction", null); - - // Assert - Assert.IsType(result); - Assert.Equal(StatusCodes.Status201Created, result.StatusCode); - Assert.Equal("SampleAction", result.ActionName); - } - - [Theory] - [InlineData("")] - [InlineData(null)] - [InlineData("SampleController")] - public void CreatedAtAction_WithActionControllerAndNullRouteValue_SetsSameValue( - string controllerName) - { - // Arrange - var controller = new TestableController(); - - // Act - var result = controller.CreatedAtAction("SampleAction", controllerName, null, null); - - // Assert - Assert.IsType(result); - Assert.Equal(StatusCodes.Status201Created, result.StatusCode); - Assert.Equal("SampleAction", result.ActionName); - Assert.Equal(controllerName, result.ControllerName); - } - - [Fact] - public void CreatedAtAction_WithActionControllerRouteValues_SetsSameValues() - { - // Arrange - var controller = new TestableController(); - var expected = new Dictionary - { - { "test", "case" }, - { "sample", "route" }, - }; - - // Act - var result = controller.CreatedAtAction( - "SampleAction", - "SampleController", - new RouteValueDictionary(expected), null); - - // Assert - Assert.IsType(result); - Assert.Equal(StatusCodes.Status201Created, result.StatusCode); - Assert.Equal("SampleAction", result.ActionName); - Assert.Equal("SampleController", result.ControllerName); - Assert.Equal(expected, result.RouteValues); - } - - [Fact] - public void CreatedAtAction_IDisposableObject_RegistersForDispose() - { - // Arrange - var mockHttpContext = new Mock(); - mockHttpContext.Setup(x => x.Response.RegisterForDispose(It.IsAny())); - - var controller = new TestableController(); - controller.ControllerContext.HttpContext = mockHttpContext.Object; - - var input = new DisposableObject(); - - // Act - var result = controller.CreatedAtAction("SampleAction", input); - - // Assert - Assert.IsType(result); - Assert.Equal(StatusCodes.Status201Created, result.StatusCode); - Assert.Equal("SampleAction", result.ActionName); - Assert.Same(input, result.Value); - mockHttpContext.Verify( - x => x.Response.RegisterForDispose(It.IsAny()), - Times.Once()); - } - - [Fact] - public void CreatedAtRoute_WithParameterRouteName_SetsResultSameRouteName() - { - // Arrange - var controller = new TestableController(); - var routeName = "SampleRoute"; - - // Act - var result = controller.CreatedAtRoute(routeName, null); - - // Assert - Assert.IsType(result); - Assert.Same(routeName, result.RouteName); - } - - [Fact] - public void CreatedAtRoute_WithParameterRouteValues_SetsResultSameRouteValues() - { - // Arrange - var controller = new TestableController(); - var expected = new Dictionary - { - { "test", "case" }, - { "sample", "route" }, - }; - - // Act - var result = controller.CreatedAtRoute(new RouteValueDictionary(expected), null); - - // Assert - Assert.IsType(result); - Assert.Equal(StatusCodes.Status201Created, result.StatusCode); - Assert.Equal(expected, result.RouteValues); - } - - [Fact] - public void CreatedAtRoute_WithParameterRouteNameAndValues_SetsResultSameProperties() - { - // Arrange - var controller = new TestableController(); - var routeName = "SampleRoute"; - var expected = new Dictionary - { - { "test", "case" }, - { "sample", "route" }, - }; - - // Act - var result = controller.CreatedAtRoute(routeName, new RouteValueDictionary(expected), null); - - // Assert - Assert.IsType(result); - Assert.Equal(StatusCodes.Status201Created, result.StatusCode); - Assert.Same(routeName, result.RouteName); - Assert.Equal(expected, result.RouteValues); - } - - [Fact] - public void CreatedAtRoute_IDisposableObject_RegistersForDispose() - { - // Arrange - var mockHttpContext = new Mock(); - mockHttpContext.Setup(x => x.Response.RegisterForDispose(It.IsAny())); - - var controller = new TestableController(); - controller.ControllerContext.HttpContext = mockHttpContext.Object; - - var input = new DisposableObject(); - - // Act - var result = controller.CreatedAtRoute("SampleRoute", input); - - // Assert - Assert.IsType(result); - Assert.Equal(StatusCodes.Status201Created, result.StatusCode); - Assert.Equal("SampleRoute", result.RouteName); - Assert.Same(input, result.Value); - mockHttpContext.Verify( - x => x.Response.RegisterForDispose(It.IsAny()), - Times.Once()); - } - - [Fact] - public void File_WithContents() - { - // Arrange - var controller = new TestableController(); - var fileContents = new byte[0]; - - // Act - var result = controller.File(fileContents, "application/pdf"); - - // Assert - Assert.NotNull(result); - Assert.Same(fileContents, result.FileContents); - Assert.Equal("application/pdf", result.ContentType.ToString()); - Assert.Equal(string.Empty, result.FileDownloadName); - } - - [Fact] - public void File_WithContentsAndFileDownloadName() - { - // Arrange - var controller = new TestableController(); - var fileContents = new byte[0]; - - // Act - var result = controller.File(fileContents, "application/pdf", "someDownloadName"); - - // Assert - Assert.NotNull(result); - Assert.Same(fileContents, result.FileContents); - Assert.Equal("application/pdf", result.ContentType.ToString()); - Assert.Equal("someDownloadName", result.FileDownloadName); - } - - [Fact] - public void File_WithPath() - { - // Arrange - var controller = new TestableController(); - var path = Path.GetFullPath("somepath"); - - // Act - var result = controller.File(path, "application/pdf"); - - // Assert - Assert.NotNull(result); - Assert.Equal(path, result.FileName); - Assert.Equal("application/pdf", result.ContentType.ToString()); - Assert.Equal(string.Empty, result.FileDownloadName); - } - - [Fact] - public void File_WithPathAndFileDownloadName() - { - // Arrange - var controller = new TestableController(); - var path = Path.GetFullPath("somepath"); - - // Act - var result = controller.File(path, "application/pdf", "someDownloadName"); - - // Assert - Assert.NotNull(result); - Assert.Equal(path, result.FileName); - Assert.Equal("application/pdf", result.ContentType.ToString()); - Assert.Equal("someDownloadName", result.FileDownloadName); - } - - [Fact] - public void File_WithStream() - { - // Arrange - var mockHttpContext = new Mock(); - mockHttpContext.Setup(x => x.Response.RegisterForDispose(It.IsAny())); - - var controller = new TestableController(); - controller.ControllerContext.HttpContext = mockHttpContext.Object; - - var fileStream = Stream.Null; - - // Act - var result = controller.File(fileStream, "application/pdf"); - - // Assert - Assert.NotNull(result); - Assert.Same(fileStream, result.FileStream); - Assert.Equal("application/pdf", result.ContentType.ToString()); - Assert.Equal(string.Empty, result.FileDownloadName); - } - - [Fact] - public void File_WithStreamAndFileDownloadName() - { - // Arrange - var mockHttpContext = new Mock(); - mockHttpContext.Setup(x => x.Response.RegisterForDispose(It.IsAny())); - - var controller = new TestableController(); - controller.ControllerContext.HttpContext = mockHttpContext.Object; - - var fileStream = Stream.Null; - - // Act - var result = controller.File(fileStream, "application/pdf", "someDownloadName"); - - // Assert - Assert.NotNull(result); - Assert.Same(fileStream, result.FileStream); - Assert.Equal("application/pdf", result.ContentType.ToString()); - Assert.Equal("someDownloadName", result.FileDownloadName); - mockHttpContext.Verify( - x => x.Response.RegisterForDispose(It.IsAny()), - Times.Once()); - } - - [Fact] - public void HttpUnauthorized_SetsStatusCode() - { - // Arrange - var controller = new TestableController(); - - // Act - var result = controller.HttpUnauthorized(); - - // Assert - Assert.IsType(result); - Assert.Equal(StatusCodes.Status401Unauthorized, result.StatusCode); - } - - [Fact] - public void HttpNotFound_SetsStatusCode() - { - // Arrange - var controller = new TestableController(); - - // Act - var result = controller.HttpNotFound(); - - // Assert - Assert.IsType(result); - Assert.Equal(StatusCodes.Status404NotFound, result.StatusCode); - } - - [Fact] - public void HttpNotFound_SetsStatusCodeAndResponseContent() - { - // Arrange - var controller = new TestableController(); - - // Act - var result = controller.HttpNotFound("Test Content"); - - // Assert - Assert.IsType(result); - Assert.Equal(StatusCodes.Status404NotFound, result.StatusCode); - Assert.Equal("Test Content", result.Value); - } - - [Fact] - public void HttpNotFound_IDisposableObject_RegistersForDispose() - { - // Arrange - var mockHttpContext = new Mock(); - mockHttpContext.Setup(x => x.Response.RegisterForDispose(It.IsAny())); - - var controller = new TestableController(); - controller.ControllerContext.HttpContext = mockHttpContext.Object; - - var input = new DisposableObject(); - - // Act - var result = controller.HttpNotFound(input); - - // Assert - Assert.IsType(result); - Assert.Equal(StatusCodes.Status404NotFound, result.StatusCode); - Assert.Same(input, result.Value); - mockHttpContext.Verify( - x => x.Response.RegisterForDispose(It.IsAny()), - Times.Once()); - } - - [Fact] - public void Ok_SetsStatusCode() - { - // Arrange - var controller = new TestableController(); - - // Act - var result = controller.Ok(); - - // Assert - Assert.IsType(result); - Assert.Equal(StatusCodes.Status200OK, result.StatusCode); - } - - [Fact] - public void Ok_WithIDisposableObject_RegistersForDispose() - { - // Arrange - var mockHttpContext = new Mock(); - mockHttpContext.Setup(x => x.Response.RegisterForDispose(It.IsAny())); - - var controller = new TestableController(); - controller.ControllerContext.HttpContext = mockHttpContext.Object; - - var input = new DisposableObject(); - - // Act - var result = controller.Ok(input); - - // Assert - Assert.IsType(result); - Assert.Equal(StatusCodes.Status200OK, result.StatusCode); - Assert.Same(input, result.Value); - mockHttpContext.Verify( - x => x.Response.RegisterForDispose(It.IsAny()), - Times.Once()); - } - - [Fact] - public void BadRequest_SetsStatusCode() - { - // Arrange - var controller = new TestableController(); - - // Act - var result = controller.HttpBadRequest(); - - // Assert - Assert.IsType(result); - Assert.Equal(StatusCodes.Status400BadRequest, result.StatusCode); - } - - [Fact] - public void BadRequest_SetsStatusCodeAndValue_Object() - { - // Arrange - var controller = new TestableController(); - var obj = new object(); - - // Act - var result = controller.HttpBadRequest(obj); - - // Assert - Assert.IsType(result); - Assert.Equal(StatusCodes.Status400BadRequest, result.StatusCode); - Assert.Equal(obj, result.Value); - } - - [Fact] - public void BadRequest_IDisposableObject_RegistersForDispose() - { - // Arrange - var mockHttpContext = new Mock(); - mockHttpContext.Setup(x => x.Response.RegisterForDispose(It.IsAny())); - - var controller = new TestableController(); - controller.ControllerContext.HttpContext = mockHttpContext.Object; - - var input = new DisposableObject(); - - // Act - var result = controller.HttpBadRequest(input); - - // Assert - Assert.IsType(result); - Assert.Equal(StatusCodes.Status400BadRequest, result.StatusCode); - Assert.Same(input, result.Value); - mockHttpContext.Verify( - x => x.Response.RegisterForDispose(It.IsAny()), - Times.Once()); - } - - [Fact] - public void BadRequest_SetsStatusCodeAndValue_ModelState() - { - // Arrange - var controller = new TestableController(); - - // Act - var result = controller.HttpBadRequest(new ModelStateDictionary()); - - // Assert - Assert.IsType(result); - Assert.Equal(StatusCodes.Status400BadRequest, result.StatusCode); - var errors = Assert.IsType(result.Value); - Assert.Equal(0, errors.Count); - } - [Theory] [MemberData(nameof(PublicNormalMethodsFromController))] public void NonActionAttribute_IsOnEveryPublicNormalMethodFromController(MethodInfo method) @@ -1047,126 +154,6 @@ namespace Microsoft.AspNet.Mvc.Test Assert.Same(model, actualViewResult.ViewData.Model); } - [Fact] - public void Controller_NoContent() - { - // Arrange - var controller = new TestableController(); - - // Act - var result = controller.NoContent(); - - // Assert - Assert.Equal(StatusCodes.Status204NoContent, result.StatusCode); - } - - [Fact] - public void Controller_Content_WithParameterContentString_SetsResultContent() - { - // Arrange - var controller = new TestableController(); - - // Act - var actualContentResult = controller.Content("TestContent"); - - // Assert - Assert.IsType(actualContentResult); - Assert.Equal("TestContent", actualContentResult.Content); - Assert.Null(actualContentResult.ContentType); - } - - [Fact] - public void Controller_Content_WithParameterContentStringAndContentType_SetsResultContentAndContentType() - { - // Arrange - var controller = new TestableController(); - - // Act - var actualContentResult = controller.Content("TestContent", "text/plain"); - - // Assert - Assert.IsType(actualContentResult); - Assert.Equal("TestContent", actualContentResult.Content); - Assert.Null(actualContentResult.ContentType.Encoding); - Assert.Equal("text/plain", actualContentResult.ContentType.ToString()); - } - - [Fact] - public void Controller_Content_WithParameterContentAndTypeAndEncoding_SetsResultContentAndTypeAndEncoding() - { - // Arrange - var controller = new TestableController(); - - // Act - var actualContentResult = controller.Content("TestContent", "text/plain", Encoding.UTF8); - - // Assert - Assert.IsType(actualContentResult); - Assert.Equal("TestContent", actualContentResult.Content); - Assert.Same(Encoding.UTF8, actualContentResult.ContentType.Encoding); - Assert.Equal("text/plain; charset=utf-8", actualContentResult.ContentType.ToString()); - } - - [Fact] - public void Controller_Content_NoContentType_DefaultEncodingIsUsed() - { - // Arrange - var contentController = new ContentController(); - var expected = MediaTypeHeaderValue.Parse("text/plain; charset=utf-8"); - - // Act - var contentResult = (ContentResult)contentController.Content_WithNoEncoding(); - - // Assert - // The default content type of ContentResult is used when the result is executed. - Assert.Null(contentResult.ContentType); - } - - [Fact] - public void Controller_Content_InvalidCharset_DefaultEncodingIsUsed() - { - // Arrange - var contentController = new ContentController(); - var contentType = "text/xml; charset=invalid; p1=p1-value"; - - // Act - var contentResult = (ContentResult)contentController.Content_WithInvalidCharset(); - - // Assert - Assert.NotNull(contentResult.ContentType); - Assert.Equal(contentType, contentResult.ContentType.ToString()); - // The default encoding of ContentResult is used when this result is executed. - Assert.Null(contentResult.ContentType.Encoding); - } - - [Fact] - public void Controller_Content_CharsetAndEncodingProvided_EncodingIsUsed() - { - // Arrange - var contentController = new ContentController(); - var contentType = MediaTypeHeaderValue.Parse("text/xml; charset=us-ascii; p1=p1-value"); - - // Act - var contentResult = (ContentResult)contentController.Content_WithEncodingInCharset_AndEncodingParameter(); - - // Assert - Assert.Equal(contentType, contentResult.ContentType); - } - - [Fact] - public void Controller_Content_CharsetInContentType_IsUsedForEncoding() - { - // Arrange - var contentController = new ContentController(); - var contentType = MediaTypeHeaderValue.Parse("text/xml; charset=us-ascii; p1=p1-value"); - - // Act - var contentResult = (ContentResult)contentController.Content_WithEncodingInCharset(); - - // Assert - Assert.Equal(contentType, contentResult.ContentType); - } - [Fact] public void Controller_Json_WithParameterValue_SetsResultData() { @@ -1245,36 +232,6 @@ namespace Microsoft.AspNet.Mvc.Test Times.Once()); } - public static IEnumerable RedirectTestData - { - get - { - yield return new object[] - { - null, - null, - }; - - yield return new object[] - { - new Dictionary { { "hello", "world" } }, - new RouteValueDictionary() { { "hello", "world" } }, - }; - - var expected2 = new Dictionary - { - { "test", "case" }, - { "sample", "route" }, - }; - - yield return new object[] - { - new RouteValueDictionary(expected2), - new RouteValueDictionary(expected2), - }; - } - } - // These tests share code with the ActionFilterAttribute tests because the various filter // implementations need to behave the same way. [Fact] @@ -1293,427 +250,6 @@ namespace Microsoft.AspNet.Mvc.Test new Mock()); } - [Fact] - public async Task TryUpdateModel_FallsBackOnEmptyPrefix_IfNotSpecified() - { - // Arrange - var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); - var valueProvider = Mock.Of(); - var binder = new Mock(); - binder.Setup(b => b.BindModelAsync(It.IsAny())) - .Callback((ModelBindingContext context) => - { - Assert.Empty(context.ModelName); - Assert.Same(valueProvider, Assert.IsType(context.ValueProvider)[0]); - - // Include and exclude should be null, resulting in property - // being included. - Assert.True(context.PropertyFilter(context, "Property1")); - Assert.True(context.PropertyFilter(context, "Property2")); - }) - .Returns(ModelBindingResult.NoResultAsync) - .Verifiable(); - - var controller = GetController(binder.Object, valueProvider); - var model = new MyModel(); - - // Act - var result = await controller.TryUpdateModelAsync(model); - - // Assert - binder.Verify(); - } - - [Fact] - public async Task TryUpdateModel_UsesModelTypeNameIfSpecified() - { - // Arrange - var modelName = "mymodel"; - - var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); - var valueProvider = Mock.Of(); - var binder = new Mock(); - binder.Setup(b => b.BindModelAsync(It.IsAny())) - .Callback((ModelBindingContext context) => - { - Assert.Same(valueProvider, Assert.IsType(context.ValueProvider)[0]); - - // Include and exclude should be null, resulting in property - // being included. - Assert.True(context.PropertyFilter(context, "Property1")); - Assert.True(context.PropertyFilter(context, "Property2")); - }) - .Returns(ModelBindingResult.NoResultAsync) - .Verifiable(); - - var controller = GetController(binder.Object, valueProvider); - var model = new MyModel(); - - // Act - var result = await controller.TryUpdateModelAsync(model, modelName); - - // Assert - binder.Verify(); - } - - [Fact] - public async Task TryUpdateModel_UsesModelValueProviderIfSpecified() - { - // Arrange - var modelName = "mymodel"; - - var valueProvider = Mock.Of(); - var binder = new Mock(); - binder.Setup(b => b.BindModelAsync(It.IsAny())) - .Callback((ModelBindingContext context) => - { - Assert.Same(valueProvider, context.ValueProvider); - - // Include and exclude should be null, resulting in property - // being included. - Assert.True(context.PropertyFilter(context, "Property1")); - Assert.True(context.PropertyFilter(context, "Property2")); - }) - .Returns(ModelBindingResult.NoResultAsync) - .Verifiable(); - - var controller = GetController(binder.Object, valueProvider: null); - var model = new MyModel(); - - // Act - var result = await controller.TryUpdateModelAsync(model, modelName, valueProvider); - - // Assert - binder.Verify(); - } - - [Fact] - public async Task TryUpdateModel_PredicateOverload_UsesPassedArguments() - { - // Arrange - var modelName = "mymodel"; - - Func includePredicate = (context, propertyName) => - string.Equals(propertyName, "include1", StringComparison.OrdinalIgnoreCase) || - string.Equals(propertyName, "include2", StringComparison.OrdinalIgnoreCase); - - var binder = new Mock(); - var valueProvider = Mock.Of(); - binder.Setup(b => b.BindModelAsync(It.IsAny())) - .Callback((ModelBindingContext context) => - { - Assert.Same(valueProvider, Assert.IsType(context.ValueProvider)[0]); - - Assert.True(context.PropertyFilter(context, "include1")); - Assert.True(context.PropertyFilter(context, "include2")); - - Assert.False(context.PropertyFilter(context, "exclude1")); - Assert.False(context.PropertyFilter(context, "exclude2")); - }) - .Returns(ModelBindingResult.NoResultAsync) - .Verifiable(); - - var controller = GetController(binder.Object, valueProvider); - var model = new MyModel(); - - // Act - await controller.TryUpdateModelAsync(model, modelName, includePredicate); - - // Assert - binder.Verify(); - } - - [Fact] - public async Task TryUpdateModel_PredicateWithValueProviderOverload_UsesPassedArguments() - { - // Arrange - var modelName = "mymodel"; - - Func includePredicate = - (context, propertyName) => string.Equals(propertyName, "include1", StringComparison.OrdinalIgnoreCase) || - string.Equals(propertyName, "include2", StringComparison.OrdinalIgnoreCase); - - var binder = new Mock(); - var valueProvider = Mock.Of(); - binder.Setup(b => b.BindModelAsync(It.IsAny())) - .Callback((ModelBindingContext context) => - { - Assert.Same(valueProvider, context.ValueProvider); - - Assert.True(context.PropertyFilter(context, "include1")); - Assert.True(context.PropertyFilter(context, "include2")); - - Assert.False(context.PropertyFilter(context, "exclude1")); - Assert.False(context.PropertyFilter(context, "exclude2")); - }) - .Returns(ModelBindingResult.NoResultAsync) - .Verifiable(); - - var controller = GetController(binder.Object, valueProvider: null); - - var model = new MyModel(); - - // Act - await controller.TryUpdateModelAsync(model, modelName, valueProvider, includePredicate); - - // Assert - binder.Verify(); - } - - [Theory] - [InlineData("")] - [InlineData("prefix")] - public async Task TryUpdateModel_IncludeExpressionOverload_UsesPassedArguments(string prefix) - { - // Arrange - var valueProvider = new Mock(); - valueProvider - .Setup(v => v.ContainsPrefix(prefix)) - .Returns(true); - - var binder = new Mock(); - binder.Setup(b => b.BindModelAsync(It.IsAny())) - .Callback((ModelBindingContext context) => - { - Assert.Same( - valueProvider.Object, - Assert.IsType(context.ValueProvider)[0]); - - Assert.True(context.PropertyFilter(context, "Property1")); - Assert.True(context.PropertyFilter(context, "Property2")); - - Assert.False(context.PropertyFilter(context, "exclude1")); - Assert.False(context.PropertyFilter(context, "exclude2")); - }) - .Returns(ModelBindingResult.NoResultAsync) - .Verifiable(); - - - var controller = GetController(binder.Object, valueProvider.Object); - var model = new MyModel(); - - // Act - await controller.TryUpdateModelAsync(model, prefix, m => m.Property1, m => m.Property2); - - // Assert - binder.Verify(); - } - - [Theory] - [InlineData("")] - [InlineData("prefix")] - public async Task - TryUpdateModel_IncludeExpressionWithValueProviderOverload_UsesPassedArguments(string prefix) - { - // Arrange - var valueProvider = new Mock(); - valueProvider - .Setup(v => v.ContainsPrefix(prefix)) - .Returns(true); - - var binder = new Mock(); - binder.Setup(b => b.BindModelAsync(It.IsAny())) - .Callback((ModelBindingContext context) => - { - Assert.Same(valueProvider.Object, context.ValueProvider); - - Assert.True(context.PropertyFilter(context, "Property1")); - Assert.True(context.PropertyFilter(context, "Property2")); - - Assert.False(context.PropertyFilter(context, "exclude1")); - Assert.False(context.PropertyFilter(context, "exclude2")); - }) - .Returns(ModelBindingResult.NoResultAsync) - .Verifiable(); - - var controller = GetController(binder.Object, valueProvider: null); - var model = new MyModel(); - - // Act - await controller.TryUpdateModelAsync(model, prefix, valueProvider.Object, m => m.Property1, m => m.Property2); - - // Assert - binder.Verify(); - } - - [Fact] - public async Task TryUpdateModelNonGeneric_PredicateWithValueProviderOverload_UsesPassedArguments() - { - // Arrange - var modelName = "mymodel"; - - Func includePredicate = - (context, propertyName) => string.Equals(propertyName, "include1", StringComparison.OrdinalIgnoreCase) || - string.Equals(propertyName, "include2", StringComparison.OrdinalIgnoreCase); - - var valueProvider = Mock.Of(); - - var binder = new Mock(); - binder.Setup(b => b.BindModelAsync(It.IsAny())) - .Callback((ModelBindingContext context) => - { - Assert.Same(valueProvider, context.ValueProvider); - - Assert.True(context.PropertyFilter(context, "include1")); - Assert.True(context.PropertyFilter(context, "include2")); - - Assert.False(context.PropertyFilter(context, "exclude1")); - Assert.False(context.PropertyFilter(context, "exclude2")); - }) - .Returns(ModelBindingResult.NoResultAsync) - .Verifiable(); - - var controller = GetController(binder.Object, valueProvider: null); - - var model = new MyModel(); - - // Act - await controller.TryUpdateModelAsync(model, model.GetType(), modelName, valueProvider, includePredicate); - - // Assert - binder.Verify(); - } - - [Fact] - public async Task TryUpdateModelNonGeneric_ModelTypeOverload_UsesPassedArguments() - { - // Arrange - var modelName = "mymodel"; - - var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); - var valueProvider = Mock.Of(); - var binder = new Mock(); - binder.Setup(b => b.BindModelAsync(It.IsAny())) - .Callback((ModelBindingContext context) => - { - Assert.Same(valueProvider, Assert.IsType(context.ValueProvider)[0]); - - // Include and exclude should be null, resulting in property - // being included. - Assert.True(context.PropertyFilter(context, "Property1")); - Assert.True(context.PropertyFilter(context, "Property2")); - }) - .Returns(ModelBindingResult.NoResultAsync) - .Verifiable(); - - var controller = GetController(binder.Object, valueProvider); - var model = new MyModel(); - - // Act - var result = await controller.TryUpdateModelAsync(model, model.GetType(), modelName); - - // Assert - binder.Verify(); - } - - [Fact] - public async Task TryUpdateModelNonGeneric_BindToBaseDeclaredType_ModelTypeOverload() - { - // Arrange - var modelName = "mymodel"; - - var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); - var valueProvider = Mock.Of(); - var binder = new Mock(); - binder.Setup(b => b.BindModelAsync(It.IsAny())) - .Callback((ModelBindingContext context) => - { - Assert.Same(valueProvider, Assert.IsType(context.ValueProvider)[0]); - - // Include and exclude should be null, resulting in property - // being included. - Assert.True(context.PropertyFilter(context, "Property1")); - Assert.True(context.PropertyFilter(context, "Property2")); - }) - .Returns(ModelBindingResult.NoResultAsync) - .Verifiable(); - - var controller = GetController(binder.Object, valueProvider); - MyModel model = new MyDerivedModel(); - - // Act - var result = await controller.TryUpdateModelAsync(model, model.GetType(), modelName); - - // Assert - binder.Verify(); - } - - [Fact] - public void ControllerExposes_RequestServices() - { - // Arrange - var controller = new TestableController(); - - var serviceProvider = Mock.Of(); - var httpContext = new Mock(); - httpContext.Setup(c => c.RequestServices) - .Returns(serviceProvider); - - controller.ControllerContext.HttpContext = httpContext.Object; - - // Act - var innerServiceProvider = controller.Resolver; - - // Assert - Assert.Same(serviceProvider, innerServiceProvider); - } - - [Fact] - public void ControllerExposes_Request() - { - // Arrange - var controller = new TestableController(); - - var request = Mock.Of(); - var httpContext = new Mock(); - httpContext.Setup(c => c.Request) - .Returns(request); - - controller.ControllerContext.HttpContext = httpContext.Object; - - // Act - var innerRequest = controller.Request; - - // Assert - Assert.Same(request, innerRequest); - } - - [Fact] - public void ControllerExposes_Response() - { - // Arrange - var controller = new TestableController(); - - var response = Mock.Of(); - var httpContext = new Mock(); - httpContext.Setup(c => c.Response) - .Returns(response); - - controller.ControllerContext.HttpContext = httpContext.Object; - - // Act - var innerResponse = controller.Response; - - // Assert - Assert.Same(response, innerResponse); - } - - [Fact] - public void ControllerExposes_RouteData() - { - // Arrange - var controller = new TestableController(); - - var routeData = Mock.Of(); - controller.ControllerContext.RouteData = routeData; - - // Act - var innerRouteData = controller.RouteData; - - // Assert - Assert.Same(routeData, innerRouteData); - } - [Fact] public void ControllerDispose_CallsDispose() { @@ -1726,116 +262,8 @@ namespace Microsoft.AspNet.Mvc.Test // Assert Assert.True(controller.DisposeCalled); } - + [Fact] - public void TryValidateModelWithValidModel_ReturnsTrue() - { - // Arrange - var binder = new Mock(); - var controller = GetController(binder.Object, valueProvider: null); - controller.ControllerContext.ValidatorProviders = new List() - { - Mock.Of(), - }; - - var model = new TryValidateModelModel(); - - // Act - var result = controller.TryValidateModel(model); - - // Assert - Assert.True(result); - Assert.True(controller.ModelState.IsValid); - } - - [Fact] - public void TryValidateModelWithInvalidModelWithPrefix_ReturnsFalse() - { - // Arrange - var model = new TryValidateModelModel(); - var validationResult = new[] - { - new ModelValidationResult(string.Empty, "Out of range!") - }; - - var validator1 = new Mock(); - - validator1.Setup(v => v.Validate(It.IsAny())) - .Returns(validationResult); - - var provider = new Mock(); - provider.Setup(v => v.GetValidators(It.IsAny())) - .Callback(c => c.Validators.Add(validator1.Object)); - - var binder = new Mock(); - var controller = GetController(binder.Object, valueProvider: null); - controller.ControllerContext.ValidatorProviders = new List() - { - provider.Object, - }; - - // Act - var result = controller.TryValidateModel(model, "Prefix"); - - // Assert - Assert.False(result); - Assert.Equal(1, controller.ModelState.Count); - var error = Assert.Single(controller.ModelState["Prefix.IntegerProperty"].Errors); - Assert.Equal("Out of range!", error.ErrorMessage); - } - - [Fact] - public void TryValidateModelWithInvalidModelNoPrefix_ReturnsFalse() - { - // Arrange - var model = new TryValidateModelModel(); - var validationResult = new[] - { - new ModelValidationResult(string.Empty, "Out of range!") - }; - - var validator1 = new Mock(); - - validator1.Setup(v => v.Validate(It.IsAny())) - .Returns(validationResult); - - var provider = new Mock(); - provider.Setup(v => v.GetValidators(It.IsAny())) - .Callback(c => c.Validators.Add(validator1.Object)); - - var binder = new Mock(); - var controller = GetController(binder.Object, valueProvider: null); - controller.ControllerContext.ValidatorProviders = new List() - { - provider.Object, - }; - - // Act - var result = controller.TryValidateModel(model); - - // Assert - Assert.False(result); - Assert.Equal(1, controller.ModelState.Count); - var error = Assert.Single(controller.ModelState["IntegerProperty"].Errors); - Assert.Equal("Out of range!", error.ErrorMessage); - } - - [Fact] - public void TryValidateModel_Succeeds_WithoutValidatorMetadata() - { - // Arrange - // Do not add a Mock validator provider to this test. Test is intended to demonstrate ease of unit testing - // and exercise DataAnnotationsModelValidatorProvider, avoiding #3586 regressions. - var model = new TryValidateModelModel(); - var controller = GetController(binder: null, valueProvider: null); - - // Act - var result = controller.TryValidateModel(model); - - // Assert - Assert.True(controller.ModelState.IsValid); - } - public void TempData_CanSetAndGetValues() { // Arrange @@ -1883,44 +311,6 @@ namespace Microsoft.AspNet.Mvc.Test return controller; } - private class MyModel - { - public string Property1 { get; set; } - public string Property2 { get; set; } - } - - private class MyDerivedModel : MyModel - { - public string Property3 { get; set; } - } - - private class User - { - public User(int id) - { - Id = id; - } - - public int Id { get; set; } - - public string Name { get; set; } - - public Address Address { get; set; } - - } - - private class Address - { - public string Street { get; set; } - public string City { get; set; } - public int Zip { get; set; } - } - - private class TryValidateModelModel - { - public int IntegerProperty { get; set; } - } - private class DisposableController : Controller { public bool DisposeCalled { get; private set; } @@ -1943,28 +333,5 @@ namespace Microsoft.AspNet.Mvc.Test throw new NotImplementedException(); } } - - private class ContentController : Controller - { - public IActionResult Content_WithNoEncoding() - { - return Content("Hello!!"); - } - - public IActionResult Content_WithEncodingInCharset() - { - return Content("Hello!!", "text/xml; charset=us-ascii; p1=p1-value"); - } - - public IActionResult Content_WithInvalidCharset() - { - return Content("Hello!!", "text/xml; charset=invalid; p1=p1-value"); - } - - public IActionResult Content_WithEncodingInCharset_AndEncodingParameter() - { - return Content("Hello!!", "text/xml; charset=invalid; p1=p1-value", Encoding.ASCII); - } - } } }