Some cleanup of ActionResults - #657

In general all properties are get/set so filters can change them.
 - some validate for not-null
 - where we use services it's get/set also

Services are resolved in the Execute method if not provided.

A few more ActionResults that return a body have the ability to set a
status code now (optional).
This commit is contained in:
Ryan Nowak 2015-01-16 01:10:09 -08:00
parent e41e5066f9
commit 692a07240c
22 changed files with 356 additions and 132 deletions

View File

@ -16,6 +16,11 @@ namespace Microsoft.AspNet.Mvc
public string ContentType { get; set; } public string ContentType { get; set; }
/// <summary>
/// Gets or sets the HTTP status code.
/// </summary>
public int? StatusCode { get; set; }
public override async Task ExecuteResultAsync([NotNull] ActionContext context) public override async Task ExecuteResultAsync([NotNull] ActionContext context)
{ {
var response = context.HttpContext.Response; var response = context.HttpContext.Response;
@ -33,6 +38,11 @@ namespace Microsoft.AspNet.Mvc
contentTypeHeader.Encoding = ContentEncoding ?? Encodings.UTF8EncodingWithoutBOM; contentTypeHeader.Encoding = ContentEncoding ?? Encodings.UTF8EncodingWithoutBOM;
response.ContentType = contentTypeHeader.ToString(); response.ContentType = contentTypeHeader.ToString();
if (StatusCode != null)
{
response.StatusCode = StatusCode.Value;
}
if (Content != null) if (Content != null)
{ {
await response.WriteAsync(Content, contentTypeHeader.Encoding); await response.WriteAsync(Content, contentTypeHeader.Encoding);

View File

@ -39,19 +39,19 @@ namespace Microsoft.AspNet.Mvc
public IUrlHelper UrlHelper { get; set; } public IUrlHelper UrlHelper { get; set; }
/// <summary> /// <summary>
/// Gets the name of the action to use for generating the URL. /// Gets or sets the name of the action to use for generating the URL.
/// </summary> /// </summary>
public string ActionName { get; private set; } public string ActionName { get; set; }
/// <summary> /// <summary>
/// Gets the name of the controller to use for generating the URL. /// Gets or sets the name of the controller to use for generating the URL.
/// </summary> /// </summary>
public string ControllerName { get; private set; } public string ControllerName { get; set; }
/// <summary> /// <summary>
/// Gets the route data to use for generating the URL. /// Gets or sets the route data to use for generating the URL.
/// </summary> /// </summary>
public IDictionary<string, object> RouteValues { get; private set; } public IDictionary<string, object> RouteValues { get; set; }
/// <inheritdoc /> /// <inheritdoc />
protected override void OnFormatting([NotNull] ActionContext context) protected override void OnFormatting([NotNull] ActionContext context)

View File

@ -47,14 +47,14 @@ namespace Microsoft.AspNet.Mvc
public IUrlHelper UrlHelper { get; set; } public IUrlHelper UrlHelper { get; set; }
/// <summary> /// <summary>
/// Gets the name of the route to use for generating the URL. /// Gets or sets the name of the route to use for generating the URL.
/// </summary> /// </summary>
public string RouteName { get; private set; } public string RouteName { get; set; }
/// <summary> /// <summary>
/// Gets the route data to use for generating the URL. /// Gets or sets the route data to use for generating the URL.
/// </summary> /// </summary>
public IDictionary<string, object> RouteValues { get; private set; } public IDictionary<string, object> RouteValues { get; set; }
/// <inheritdoc /> /// <inheritdoc />
protected override void OnFormatting([NotNull] ActionContext context) protected override void OnFormatting([NotNull] ActionContext context)

View File

@ -10,6 +10,8 @@ namespace Microsoft.AspNet.Mvc
/// </summary> /// </summary>
public class CreatedResult : ObjectResult public class CreatedResult : ObjectResult
{ {
private string _location;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="CreatedResult"/> class with the values /// Initializes a new instance of the <see cref="CreatedResult"/> class with the values
/// provided. /// provided.
@ -24,9 +26,24 @@ namespace Microsoft.AspNet.Mvc
} }
/// <summary> /// <summary>
/// Gets the location at which the content has been created. /// Gets or sets the location at which the content has been created.
/// </summary> /// </summary>
public string Location { get; private set; } public string Location
{
get
{
return _location;
}
set
{
if (value == null)
{
throw new ArgumentNullException("value");
}
_location = value;
}
}
/// <inheritdoc /> /// <inheritdoc />
protected override void OnFormatting([NotNull] ActionContext context) protected override void OnFormatting([NotNull] ActionContext context)

View File

@ -1,6 +1,7 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.Http; using Microsoft.AspNet.Http;
@ -13,6 +14,19 @@ namespace Microsoft.AspNet.Mvc
/// </summary> /// </summary>
public class FileContentResult : FileResult public class FileContentResult : FileResult
{ {
private byte[] _fileContents;
/// <summary>
/// Creates a new <see cref="FileContentResult"/> instance with
/// the provided <paramref name="fileContents"/>.
/// </summary>
/// <param name="fileContents">The bytes that represent the file contents.</param>
public FileContentResult([NotNull] byte[] fileContents)
: base(contentType: null)
{
FileContents = fileContents;
}
/// <summary> /// <summary>
/// Creates a new <see cref="FileContentResult"/> instance with /// Creates a new <see cref="FileContentResult"/> instance with
/// the provided <paramref name="fileContents"/> and the /// the provided <paramref name="fileContents"/> and the
@ -27,9 +41,24 @@ namespace Microsoft.AspNet.Mvc
} }
/// <summary> /// <summary>
/// Gets the file contents. /// Gets or sets the file contents.
/// </summary> /// </summary>
public byte[] FileContents { get; private set; } public byte[] FileContents
{
get
{
return _fileContents;
}
set
{
if (value == null)
{
throw new ArgumentNullException("value");
}
_fileContents = value;
}
}
/// <inheritdoc /> /// <inheritdoc />
protected override Task WriteFileAsync(HttpResponse response, CancellationToken cancellation) protected override Task WriteFileAsync(HttpResponse response, CancellationToken cancellation)

View File

@ -15,7 +15,7 @@ using Microsoft.Framework.DependencyInjection;
namespace Microsoft.AspNet.Mvc namespace Microsoft.AspNet.Mvc
{ {
/// <summary> /// <summary>
/// Represents an <see cref="ActionResult"/> that when executed will /// An <see cref="ActionResult"/> that when executed will
/// write a file from disk to the response using mechanisms provided /// write a file from disk to the response using mechanisms provided
/// by the host. /// by the host.
/// </summary> /// </summary>
@ -23,16 +23,17 @@ namespace Microsoft.AspNet.Mvc
{ {
private const int DefaultBufferSize = 0x1000; private const int DefaultBufferSize = 0x1000;
private string _fileName;
/// <summary> /// <summary>
/// Creates a new <see cref="FilePathResult"/> instance with /// Creates a new <see cref="FilePathResult"/> instance with
/// the provided <paramref name="fileName"/> and the /// the provided <paramref name="fileName"/>
/// provided <paramref name="contentType"/>.
/// </summary> /// </summary>
/// <param name="fileName">The path to the file. The path must be an absolute /// <param name="fileName">The path to the file. The path must be an absolute
/// path. Relative and virtual paths are not supported.</param> /// path. Relative and virtual paths are not supported.</param>
/// <param name="contentType">The Content-Type header of the response.</param> /// <param name="contentType">The Content-Type header of the response.</param>
public FilePathResult([NotNull] string fileName, [NotNull] string contentType) public FilePathResult([NotNull] string fileName)
: base(contentType) : base(contentType: null)
{ {
FileName = fileName; FileName = fileName;
} }
@ -45,25 +46,36 @@ namespace Microsoft.AspNet.Mvc
/// <param name="fileName">The path to the file. The path must be an absolute /// <param name="fileName">The path to the file. The path must be an absolute
/// path. Relative and virtual paths are not supported.</param> /// path. Relative and virtual paths are not supported.</param>
/// <param name="contentType">The Content-Type header of the response.</param> /// <param name="contentType">The Content-Type header of the response.</param>
public FilePathResult( public FilePathResult([NotNull] string fileName, string contentType)
[NotNull] string fileName,
[NotNull] string contentType,
[NotNull] IFileSystem fileSystem)
: base(contentType) : base(contentType)
{ {
FileName = fileName; FileName = fileName;
FileSystem = fileSystem;
} }
/// <summary> /// <summary>
/// Gets the path to the file that will be sent back as the response. /// Gets or sets the path to the file that will be sent back as the response.
/// </summary> /// </summary>
public string FileName { get; private set; } public string FileName
{
get
{
return _fileName;
}
set
{
if (value == null)
{
throw new ArgumentNullException("value");
}
_fileName = value;
}
}
/// <summary> /// <summary>
/// Gets the <see cref="IFileSystem"/> used to resolve paths. /// Gets or sets the <see cref="IFileSystem"/> used to resolve paths.
/// </summary> /// </summary>
public IFileSystem FileSystem { get; private set; } public IFileSystem FileSystem { get; set; }
/// <inheritdoc /> /// <inheritdoc />
protected override Task WriteFileAsync(HttpResponse response, CancellationToken cancellation) protected override Task WriteFileAsync(HttpResponse response, CancellationToken cancellation)

View File

@ -23,15 +23,15 @@ namespace Microsoft.AspNet.Mvc
/// the provided <paramref name="contentType"/>. /// the provided <paramref name="contentType"/>.
/// </summary> /// </summary>
/// <param name="contentType">The Content-Type header of the response.</param> /// <param name="contentType">The Content-Type header of the response.</param>
protected FileResult([NotNull] string contentType) protected FileResult(string contentType)
{ {
ContentType = contentType; ContentType = contentType;
} }
/// <summary> /// <summary>
/// Gets the Content-Type header value that will be written to the response. /// Gets or sets the Content-Type header value that will be written to the response.
/// </summary> /// </summary>
public string ContentType { get; private set; } public string ContentType { get; set; }
/// <summary> /// <summary>
/// Gets the file name that will be used in the Content-Disposition header of the response. /// Gets the file name that will be used in the Content-Disposition header of the response.
@ -46,7 +46,11 @@ namespace Microsoft.AspNet.Mvc
public override Task ExecuteResultAsync([NotNull] ActionContext context) public override Task ExecuteResultAsync([NotNull] ActionContext context)
{ {
var response = context.HttpContext.Response; var response = context.HttpContext.Response;
response.ContentType = ContentType;
if (ContentType != null)
{
response.ContentType = ContentType;
}
if (!string.IsNullOrEmpty(FileDownloadName)) if (!string.IsNullOrEmpty(FileDownloadName))
{ {

View File

@ -1,6 +1,7 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -17,6 +18,19 @@ namespace Microsoft.AspNet.Mvc
// default buffer size as defined in BufferedStream type // default buffer size as defined in BufferedStream type
private const int BufferSize = 0x1000; private const int BufferSize = 0x1000;
private Stream _fileStream;
/// <summary>
/// Creates a new <see cref="FileStreamResult"/> instance with
/// the provided <paramref name="fileStream"/>.
/// </summary>
/// <param name="fileStream">The stream with the file.</param>
public FileStreamResult([NotNull] Stream fileStream)
: base(contentType: null)
{
FileStream = fileStream;
}
/// <summary> /// <summary>
/// Creates a new <see cref="FileStreamResult"/> instance with /// Creates a new <see cref="FileStreamResult"/> instance with
/// the provided <paramref name="fileStream"/> and the /// the provided <paramref name="fileStream"/> and the
@ -31,9 +45,24 @@ namespace Microsoft.AspNet.Mvc
} }
/// <summary> /// <summary>
/// Gets the stream with the file that will be sent back as the response. /// Gets or sets the stream with the file that will be sent back as the response.
/// </summary> /// </summary>
public Stream FileStream { get; private set; } public Stream FileStream
{
get
{
return _fileStream;
}
set
{
if (value == null)
{
throw new ArgumentNullException("value");
}
_fileStream = value;
}
}
/// <inheritdoc /> /// <inheritdoc />
protected async override Task WriteFileAsync(HttpResponse response, CancellationToken cancellation) protected async override Task WriteFileAsync(HttpResponse response, CancellationToken cancellation)

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.DependencyInjection;
@ -55,6 +56,11 @@ namespace Microsoft.AspNet.Mvc
/// </summary> /// </summary>
public IOutputFormatter Formatter { get; set; } public IOutputFormatter Formatter { get; set; }
/// <summary>
/// Gets or sets the HTTP status code.
/// </summary>
public int? StatusCode { get; set; }
/// <summary> /// <summary>
/// Gets or sets the value to be formatted. /// Gets or sets the value to be formatted.
/// </summary> /// </summary>
@ -86,7 +92,16 @@ namespace Microsoft.AspNet.Mvc
Object = Value, Object = Value,
}; };
// JsonResult expects to always find a formatter, in contrast with ObjectResult, which might return
// a 406.
var formatter = SelectFormatter(objectResult, formatterContext); var formatter = SelectFormatter(objectResult, formatterContext);
Debug.Assert(formatter != null);
if (StatusCode != null)
{
context.HttpContext.Response.StatusCode = StatusCode.Value;
}
await formatter.WriteAsync(formatterContext); await formatter.WriteAsync(formatterContext);
} }

View File

@ -1,23 +1,13 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#if ASPNET50
using System.Net;
#endif
namespace Microsoft.AspNet.Mvc namespace Microsoft.AspNet.Mvc
{ {
public class NoContentResult : ActionResult public class NoContentResult : HttpStatusCodeResult
{ {
public override void ExecuteResult([NotNull] ActionContext context) public NoContentResult()
: base(204)
{ {
var response = context.HttpContext.Response;
#if ASPNET50
response.StatusCode = (int)HttpStatusCode.NoContent;
#else
response.StatusCode = 204;
#endif
} }
} }
} }

View File

@ -14,6 +14,13 @@ namespace Microsoft.AspNet.Mvc
{ {
public class ObjectResult : ActionResult public class ObjectResult : ActionResult
{ {
public ObjectResult(object value)
{
Value = value;
Formatters = new List<IOutputFormatter>();
ContentTypes = new List<MediaTypeHeaderValue>();
}
public object Value { get; set; } public object Value { get; set; }
public IList<IOutputFormatter> Formatters { get; set; } public IList<IOutputFormatter> Formatters { get; set; }
@ -27,13 +34,6 @@ namespace Microsoft.AspNet.Mvc
/// </summary> /// </summary>
public int? StatusCode { get; set; } public int? StatusCode { get; set; }
public ObjectResult(object value)
{
Value = value;
Formatters = new List<IOutputFormatter>();
ContentTypes = new List<MediaTypeHeaderValue>();
}
public override async Task ExecuteResultAsync(ActionContext context) public override async Task ExecuteResultAsync(ActionContext context)
{ {
var formatters = GetDefaultFormatters(context); var formatters = GetDefaultFormatters(context);

View File

@ -13,6 +13,11 @@ namespace Microsoft.AspNet.Mvc
/// </summary> /// </summary>
public class PartialViewResult : ActionResult public class PartialViewResult : ActionResult
{ {
/// <summary>
/// Gets or sets the HTTP status code.
/// </summary>
public int? StatusCode { get; set; }
/// <summary> /// <summary>
/// Gets or sets the name of the partial view to render. /// Gets or sets the name of the partial view to render.
/// </summary> /// </summary>
@ -44,6 +49,11 @@ namespace Microsoft.AspNet.Mvc
.EnsureSuccessful() .EnsureSuccessful()
.View; .View;
if (StatusCode != null)
{
context.HttpContext.Response.StatusCode = StatusCode.Value;
}
using (view as IDisposable) using (view as IDisposable)
{ {
await ViewExecutor.ExecuteAsync(view, context, ViewData, contentType: null); await ViewExecutor.ExecuteAsync(view, context, ViewData, contentType: null);

View File

@ -9,12 +9,14 @@ namespace Microsoft.AspNet.Mvc
{ {
public class RedirectResult : ActionResult public class RedirectResult : ActionResult
{ {
public RedirectResult(string url) private string _url;
public RedirectResult([NotNull] string url)
: this(url, permanent: false) : this(url, permanent: false)
{ {
} }
public RedirectResult(string url, bool permanent) public RedirectResult([NotNull] string url, bool permanent)
{ {
if (string.IsNullOrEmpty(url)) if (string.IsNullOrEmpty(url))
{ {
@ -25,18 +27,33 @@ namespace Microsoft.AspNet.Mvc
Url = url; Url = url;
} }
public bool Permanent { get; private set; } public bool Permanent { get; set; }
public string Url { get; private set; } public string Url
{
get
{
return _url;
}
set
{
if (string.IsNullOrEmpty(value))
{
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, "value");
}
_url = value;
}
}
public IUrlHelper UrlHelper { get; set; }
public override void ExecuteResult([NotNull] ActionContext context) public override void ExecuteResult([NotNull] ActionContext context)
{ {
var destinationUrl = Url; var urlHelper = GetUrlHelper(context);
var urlHelper = context.HttpContext
.RequestServices
.GetRequiredService<IUrlHelper>();
// IsLocalUrl is called to handle Urls starting with '~/'. // IsLocalUrl is called to handle Urls starting with '~/'.
var destinationUrl = Url;
if (urlHelper.IsLocalUrl(destinationUrl)) if (urlHelper.IsLocalUrl(destinationUrl))
{ {
destinationUrl = urlHelper.Content(Url); destinationUrl = urlHelper.Content(Url);
@ -44,5 +61,10 @@ namespace Microsoft.AspNet.Mvc
context.HttpContext.Response.Redirect(destinationUrl, Permanent); context.HttpContext.Response.Redirect(destinationUrl, Permanent);
} }
private IUrlHelper GetUrlHelper(ActionContext context)
{
return UrlHelper ?? context.HttpContext.RequestServices.GetRequiredService<IUrlHelper>();
}
} }
} }

View File

@ -4,41 +4,47 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.AspNet.Mvc.Core; using Microsoft.AspNet.Mvc.Core;
using Microsoft.Framework.DependencyInjection;
namespace Microsoft.AspNet.Mvc namespace Microsoft.AspNet.Mvc
{ {
public class RedirectToActionResult : ActionResult public class RedirectToActionResult : ActionResult
{ {
public RedirectToActionResult([NotNull] IUrlHelper urlHelper, string actionName, public RedirectToActionResult(
string controllerName, IDictionary<string, object> routeValues) string actionName,
: this(urlHelper, actionName, controllerName, routeValues, permanent: false) string controllerName,
IDictionary<string, object> routeValues)
: this(actionName, controllerName, routeValues, permanent: false)
{ {
} }
public RedirectToActionResult([NotNull] IUrlHelper urlHelper, string actionName, public RedirectToActionResult(
string controllerName, IDictionary<string, object> routeValues, bool permanent) string actionName,
string controllerName,
IDictionary<string, object> routeValues,
bool permanent)
{ {
UrlHelper = urlHelper;
ActionName = actionName; ActionName = actionName;
ControllerName = controllerName; ControllerName = controllerName;
RouteValues = routeValues; RouteValues = routeValues;
Permanent = permanent; Permanent = permanent;
} }
public IUrlHelper UrlHelper { get; private set; } public IUrlHelper UrlHelper { get; set; }
public string ActionName { get; private set; } public string ActionName { get; set; }
public string ControllerName { get; private set; } public string ControllerName { get; set; }
public IDictionary<string, object> RouteValues { get; private set; } public IDictionary<string, object> RouteValues { get; set; }
public bool Permanent { get; private set; } public bool Permanent { get; set; }
public override void ExecuteResult([NotNull] ActionContext context) public override void ExecuteResult([NotNull] ActionContext context)
{ {
var destinationUrl = UrlHelper.Action(ActionName, ControllerName, RouteValues); var urlHelper = GetUrlHelper(context);
var destinationUrl = urlHelper.Action(ActionName, ControllerName, RouteValues);
if (string.IsNullOrEmpty(destinationUrl)) if (string.IsNullOrEmpty(destinationUrl))
{ {
throw new InvalidOperationException(Resources.NoRoutesMatched); throw new InvalidOperationException(Resources.NoRoutesMatched);
@ -46,5 +52,10 @@ namespace Microsoft.AspNet.Mvc
context.HttpContext.Response.Redirect(destinationUrl, Permanent); context.HttpContext.Response.Redirect(destinationUrl, Permanent);
} }
private IUrlHelper GetUrlHelper(ActionContext context)
{
return UrlHelper ?? context.HttpContext.RequestServices.GetRequiredService<IUrlHelper>();
}
} }
} }

View File

@ -4,47 +4,47 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.AspNet.Mvc.Core; using Microsoft.AspNet.Mvc.Core;
using Microsoft.Framework.DependencyInjection;
namespace Microsoft.AspNet.Mvc namespace Microsoft.AspNet.Mvc
{ {
public class RedirectToRouteResult : ActionResult public class RedirectToRouteResult : ActionResult
{ {
public RedirectToRouteResult([NotNull] IUrlHelper urlHelper, public RedirectToRouteResult(object routeValues)
object routeValues) : this(routeName: null, routeValues: routeValues)
: this(urlHelper, routeName: null, routeValues: routeValues)
{ {
} }
public RedirectToRouteResult([NotNull] IUrlHelper urlHelper, public RedirectToRouteResult(
string routeName, string routeName,
object routeValues) object routeValues)
: this(urlHelper, routeName, routeValues, permanent: false) : this(routeName, routeValues, permanent: false)
{ {
} }
public RedirectToRouteResult([NotNull] IUrlHelper urlHelper, public RedirectToRouteResult(
string routeName, string routeName,
object routeValues, object routeValues,
bool permanent) bool permanent)
{ {
UrlHelper = urlHelper;
RouteName = routeName; RouteName = routeName;
RouteValues = TypeHelper.ObjectToDictionary(routeValues); RouteValues = TypeHelper.ObjectToDictionary(routeValues);
Permanent = permanent; Permanent = permanent;
} }
public IUrlHelper UrlHelper { get; private set; } public IUrlHelper UrlHelper { get; set; }
public string RouteName { get; private set; } public string RouteName { get; set; }
public IDictionary<string, object> RouteValues { get; private set; } public IDictionary<string, object> RouteValues { get; set; }
public bool Permanent { get; private set; } public bool Permanent { get; set; }
public override void ExecuteResult([NotNull] ActionContext context) public override void ExecuteResult([NotNull] ActionContext context)
{ {
var destinationUrl = UrlHelper.RouteUrl(RouteValues); var urlHelper = GetUrlHelper(context);
var destinationUrl = urlHelper.RouteUrl(RouteValues);
if (string.IsNullOrEmpty(destinationUrl)) if (string.IsNullOrEmpty(destinationUrl))
{ {
throw new InvalidOperationException(Resources.NoRoutesMatched); throw new InvalidOperationException(Resources.NoRoutesMatched);
@ -52,5 +52,10 @@ namespace Microsoft.AspNet.Mvc
context.HttpContext.Response.Redirect(destinationUrl, Permanent); context.HttpContext.Response.Redirect(destinationUrl, Permanent);
} }
private IUrlHelper GetUrlHelper(ActionContext context)
{
return UrlHelper ?? context.HttpContext.RequestServices.GetRequiredService<IUrlHelper>();
}
} }
} }

View File

@ -13,6 +13,11 @@ namespace Microsoft.AspNet.Mvc
/// </summary> /// </summary>
public class ViewResult : ActionResult public class ViewResult : ActionResult
{ {
/// <summary>
/// Gets or sets the HTTP status code.
/// </summary>
public int? StatusCode { get; set; }
/// <summary> /// <summary>
/// Gets or sets the name of the view to render. /// Gets or sets the name of the view to render.
/// </summary> /// </summary>
@ -44,6 +49,11 @@ namespace Microsoft.AspNet.Mvc
.EnsureSuccessful() .EnsureSuccessful()
.View; .View;
if (StatusCode != null)
{
context.HttpContext.Response.StatusCode = StatusCode.Value;
}
using (view as IDisposable) using (view as IDisposable)
{ {
await ViewExecutor.ExecuteAsync(view, context, ViewData, contentType: null); await ViewExecutor.ExecuteAsync(view, context, ViewData, contentType: null);

View File

@ -398,8 +398,10 @@ namespace Microsoft.AspNet.Mvc
public virtual RedirectToActionResult RedirectToAction(string actionName, string controllerName, public virtual RedirectToActionResult RedirectToAction(string actionName, string controllerName,
object routeValues) object routeValues)
{ {
return new RedirectToActionResult(Url, actionName, controllerName, return new RedirectToActionResult(actionName, controllerName, TypeHelper.ObjectToDictionary(routeValues))
TypeHelper.ObjectToDictionary(routeValues)); {
UrlHelper = Url,
};
} }
/// <summary> /// <summary>
@ -453,8 +455,14 @@ namespace Microsoft.AspNet.Mvc
public virtual RedirectToActionResult RedirectToActionPermanent(string actionName, string controllerName, public virtual RedirectToActionResult RedirectToActionPermanent(string actionName, string controllerName,
object routeValues) object routeValues)
{ {
return new RedirectToActionResult(Url, actionName, controllerName, return new RedirectToActionResult(
TypeHelper.ObjectToDictionary(routeValues), permanent: true); actionName,
controllerName,
TypeHelper.ObjectToDictionary(routeValues),
permanent: true)
{
UrlHelper = Url,
};
} }
/// <summary> /// <summary>
@ -489,7 +497,10 @@ namespace Microsoft.AspNet.Mvc
[NonAction] [NonAction]
public virtual RedirectToRouteResult RedirectToRoute(string routeName, object routeValues) public virtual RedirectToRouteResult RedirectToRoute(string routeName, object routeValues)
{ {
return new RedirectToRouteResult(Url, routeName, routeValues); return new RedirectToRouteResult(routeName, routeValues)
{
UrlHelper = Url,
};
} }
/// <summary> /// <summary>
@ -526,7 +537,10 @@ namespace Microsoft.AspNet.Mvc
[NonAction] [NonAction]
public virtual RedirectToRouteResult RedirectToRoutePermanent(string routeName, object routeValues) public virtual RedirectToRouteResult RedirectToRoutePermanent(string routeName, object routeValues)
{ {
return new RedirectToRouteResult(Url, routeName, routeValues, permanent: true); return new RedirectToRouteResult(routeName, routeValues, permanent: true)
{
UrlHelper = Url,
};
} }
/// <summary> /// <summary>

View File

@ -359,7 +359,10 @@ namespace System.Web.Http
[NonAction] [NonAction]
public virtual RedirectToRouteResult RedirectToRoute([NotNull] string routeName, [NotNull] object routeValues) public virtual RedirectToRouteResult RedirectToRoute([NotNull] string routeName, [NotNull] object routeValues)
{ {
return new RedirectToRouteResult(Url, routeName, routeValues); return new RedirectToRouteResult(routeName, routeValues)
{
UrlHelper = Url,
};
} }
/// <summary> /// <summary>

View File

@ -34,8 +34,11 @@ namespace Microsoft.AspNet.Mvc
{ {
// Arrange // Arrange
var path = Path.GetFullPath(Path.Combine("TestFiles", "FilePathResultTestFile.txt")); var path = Path.GetFullPath(Path.Combine("TestFiles", "FilePathResultTestFile.txt"));
var fileSystem = new PhysicalFileSystem(Path.GetFullPath("."));
var result = new FilePathResult(path, "text/plain", fileSystem); var result = new FilePathResult(path, "text/plain")
{
FileSystem = new PhysicalFileSystem(Path.GetFullPath(".")),
};
var httpContext = new DefaultHttpContext(); var httpContext = new DefaultHttpContext();
httpContext.Response.Body = new MemoryStream(); httpContext.Response.Body = new MemoryStream();
@ -86,8 +89,11 @@ namespace Microsoft.AspNet.Mvc
{ {
// Arrange // Arrange
var path = Path.GetFullPath(Path.Combine("TestFiles", "FilePathResultTestFile.txt")); var path = Path.GetFullPath(Path.Combine("TestFiles", "FilePathResultTestFile.txt"));
var fileSystem = new PhysicalFileSystem(Path.GetFullPath("."));
var result = new FilePathResult(path, "text/plain", fileSystem); var result = new FilePathResult(path, "text/plain")
{
FileSystem = new PhysicalFileSystem(Path.GetFullPath(".")),
};
var sendFileMock = new Mock<IHttpSendFileFeature>(); var sendFileMock = new Mock<IHttpSendFileFeature>();
sendFileMock sendFileMock
@ -117,8 +123,10 @@ namespace Microsoft.AspNet.Mvc
path = path.Replace('/', '\\'); path = path.Replace('/', '\\');
// Point the FileSystemRoot to a subfolder // Point the FileSystemRoot to a subfolder
var fileSystem = new PhysicalFileSystem(Path.GetFullPath("Utils")); var result = new FilePathResult(path, "text/plain")
var result = new FilePathResult(path, "text/plain", fileSystem); {
FileSystem = new PhysicalFileSystem(Path.GetFullPath("Utils")),
};
var httpContext = new DefaultHttpContext(); var httpContext = new DefaultHttpContext();
httpContext.Response.Body = new MemoryStream(); httpContext.Response.Body = new MemoryStream();
@ -144,8 +152,10 @@ namespace Microsoft.AspNet.Mvc
path = path.Replace(@"\", "/"); path = path.Replace(@"\", "/");
// Point the FileSystemRoot to a subfolder // Point the FileSystemRoot to a subfolder
var fileSystem = new PhysicalFileSystem(Path.GetFullPath("Utils")); var result = new FilePathResult(path, "text/plain")
var result = new FilePathResult(path, "text/plain", fileSystem); {
FileSystem = new PhysicalFileSystem(Path.GetFullPath("Utils")),
};
var httpContext = new DefaultHttpContext(); var httpContext = new DefaultHttpContext();
httpContext.Response.Body = new MemoryStream(); httpContext.Response.Body = new MemoryStream();
@ -195,7 +205,10 @@ namespace Microsoft.AspNet.Mvc
// Arrange // Arrange
var fileSystem = new PhysicalFileSystem(Path.GetFullPath("./TestFiles")); var fileSystem = new PhysicalFileSystem(Path.GetFullPath("./TestFiles"));
var expectedPath = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), relativePathToFile)); var expectedPath = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), relativePathToFile));
var filePathResult = new FilePathResult(path, "text/plain", fileSystem); var filePathResult = new FilePathResult(path, "text/plain")
{
FileSystem = fileSystem,
};
// Act // Act
var result = filePathResult.ResolveFilePath(fileSystem); var result = filePathResult.ResolveFilePath(fileSystem);
@ -213,7 +226,10 @@ namespace Microsoft.AspNet.Mvc
// Arrange // Arrange
var fileSystem = new PhysicalFileSystem(Path.GetFullPath("./TestFiles")); var fileSystem = new PhysicalFileSystem(Path.GetFullPath("./TestFiles"));
var expectedPath = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), relativePathToFile)); var expectedPath = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), relativePathToFile));
var filePathResult = new FilePathResult(path, "text/plain", fileSystem); var filePathResult = new FilePathResult(path, "text/plain")
{
FileSystem = fileSystem,
};
// Act // Act
var ex = Assert.Throws<FileNotFoundException>(() => filePathResult.ResolveFilePath(fileSystem)); var ex = Assert.Throws<FileNotFoundException>(() => filePathResult.ResolveFilePath(fileSystem));
@ -257,7 +273,11 @@ namespace Microsoft.AspNet.Mvc
// Point the IFileSystem root to a different subfolder // Point the IFileSystem root to a different subfolder
var fileSystem = new PhysicalFileSystem(Path.GetFullPath("./Utils")); var fileSystem = new PhysicalFileSystem(Path.GetFullPath("./Utils"));
var filePathResult = new FilePathResult(path, "text/plain", fileSystem); var filePathResult = new FilePathResult(path, "text/plain")
{
FileSystem = fileSystem,
};
var expectedFileName = path.TrimStart('~').Replace('\\', '/'); var expectedFileName = path.TrimStart('~').Replace('\\', '/');
var expectedMessage = "Could not find file: " + expectedFileName; var expectedMessage = "Could not find file: " + expectedFileName;
@ -300,7 +320,10 @@ namespace Microsoft.AspNet.Mvc
public void NormalizePath_ConvertsBackSlashes_IntoForwardSlashes(string path, string expectedPath) public void NormalizePath_ConvertsBackSlashes_IntoForwardSlashes(string path, string expectedPath)
{ {
// Arrange // Arrange
var fileResult = new FilePathResult(path, "text/plain", Mock.Of<IFileSystem>()); var fileResult = new FilePathResult(path, "text/plain")
{
FileSystem = Mock.Of<IFileSystem>(),
};
// Act // Act
var normalizedPath = fileResult.NormalizePath(path); var normalizedPath = fileResult.NormalizePath(path);
@ -317,7 +340,10 @@ namespace Microsoft.AspNet.Mvc
public void NormalizePath_ConvertsVirtualPaths_IntoRelativePaths(string path, string expectedPath) public void NormalizePath_ConvertsVirtualPaths_IntoRelativePaths(string path, string expectedPath)
{ {
// Arrange // Arrange
var fileResult = new FilePathResult(path, "text/plain", Mock.Of<IFileSystem>()); var fileResult = new FilePathResult(path, "text/plain")
{
FileSystem = Mock.Of<IFileSystem>(),
};
// Act // Act
var normalizedPath = fileResult.NormalizePath(path); var normalizedPath = fileResult.NormalizePath(path);
@ -334,7 +360,10 @@ namespace Microsoft.AspNet.Mvc
public void NormalizePath_DoesNotConvert_InvalidVirtualPathsIntoRelativePaths(string path) public void NormalizePath_DoesNotConvert_InvalidVirtualPathsIntoRelativePaths(string path)
{ {
// Arrange // Arrange
var fileResult = new FilePathResult(path, "text/plain", Mock.Of<IFileSystem>()); var fileResult = new FilePathResult(path, "text/plain")
{
FileSystem = Mock.Of<IFileSystem>(),
};
// Act // Act
var normalizedPath = fileResult.NormalizePath(path); var normalizedPath = fileResult.NormalizePath(path);
@ -351,7 +380,10 @@ namespace Microsoft.AspNet.Mvc
public void IsPathRooted_ReturnsTrue_ForAbsolutePaths(string path) public void IsPathRooted_ReturnsTrue_ForAbsolutePaths(string path)
{ {
// Arrange // Arrange
var fileResult = new FilePathResult(path, "text/plain", Mock.Of<IFileSystem>()); var fileResult = new FilePathResult(path, "text/plain")
{
FileSystem = Mock.Of<IFileSystem>(),
};
// Act // Act
var isRooted = fileResult.IsPathRooted(path); var isRooted = fileResult.IsPathRooted(path);
@ -393,7 +425,10 @@ namespace Microsoft.AspNet.Mvc
public void IsPathRooted_ReturnsFalse_ForRelativePaths(string path) public void IsPathRooted_ReturnsFalse_ForRelativePaths(string path)
{ {
// Arrange // Arrange
var fileResult = new FilePathResult(path, "text/plain", Mock.Of<IFileSystem>()); var fileResult = new FilePathResult(path, "text/plain")
{
FileSystem = Mock.Of<IFileSystem>(),
};
// Act // Act
var isRooted = fileResult.IsPathRooted(path); var isRooted = fileResult.IsPathRooted(path);

View File

@ -25,8 +25,11 @@ namespace Microsoft.AspNet.Mvc.Core.Test.ActionResults
var actionContext = new ActionContext(httpContext.Object, var actionContext = new ActionContext(httpContext.Object,
new RouteData(), new RouteData(),
new ActionDescriptor()); new ActionDescriptor());
IUrlHelper urlHelper = GetMockUrlHelper(expectedUrl); var urlHelper = GetMockUrlHelper(expectedUrl);
RedirectToActionResult result = new RedirectToActionResult(urlHelper, "SampleAction", null, null); var result = new RedirectToActionResult("SampleAction", null, null)
{
UrlHelper = urlHelper,
};
// Act // Act
await result.ExecuteResultAsync(actionContext); await result.ExecuteResultAsync(actionContext);
@ -48,8 +51,11 @@ namespace Microsoft.AspNet.Mvc.Core.Test.ActionResults
new RouteData(), new RouteData(),
new ActionDescriptor()); new ActionDescriptor());
IUrlHelper urlHelper = GetMockUrlHelper(returnValue: null); var urlHelper = GetMockUrlHelper(returnValue: null);
RedirectToActionResult result = new RedirectToActionResult(urlHelper, null, null, null); var result = new RedirectToActionResult(null, null, null)
{
UrlHelper = urlHelper,
};
// Act & Assert // Act & Assert
ExceptionAssert.ThrowsAsync<InvalidOperationException>( ExceptionAssert.ThrowsAsync<InvalidOperationException>(

View File

@ -27,10 +27,12 @@ namespace Microsoft.AspNet.Mvc.Core
var actionContext = new ActionContext(httpContext.Object, var actionContext = new ActionContext(httpContext.Object,
new RouteData(), new RouteData(),
new ActionDescriptor()); new ActionDescriptor());
IUrlHelper urlHelper = GetMockUrlHelper(expectedUrl);
RedirectToRouteResult result = new RedirectToRouteResult(urlHelper, var urlHelper = GetMockUrlHelper(expectedUrl);
null, var result = new RedirectToRouteResult(null, TypeHelper.ObjectToDictionary(values))
TypeHelper.ObjectToDictionary(values)); {
UrlHelper = urlHelper,
};
// Act // Act
await result.ExecuteResultAsync(actionContext); await result.ExecuteResultAsync(actionContext);
@ -52,10 +54,11 @@ namespace Microsoft.AspNet.Mvc.Core
new RouteData(), new RouteData(),
new ActionDescriptor()); new ActionDescriptor());
IUrlHelper urlHelper = GetMockUrlHelper(returnValue: null); var urlHelper = GetMockUrlHelper(returnValue: null);
RedirectToRouteResult result = new RedirectToRouteResult(urlHelper, var result = new RedirectToRouteResult(null, new Dictionary<string, object>())
null, {
new Dictionary<string, object>()); UrlHelper = urlHelper,
};
// Act & Assert // Act & Assert
ExceptionAssert.ThrowsAsync<InvalidOperationException>( ExceptionAssert.ThrowsAsync<InvalidOperationException>(

View File

@ -85,7 +85,7 @@ namespace Microsoft.AspNet.Mvc
[Theory] [Theory]
[InlineData("/Created_1", "<html>CreatedBody</html>")] [InlineData("/Created_1", "<html>CreatedBody</html>")]
[InlineData(null, null)] [InlineData("/Created_2", null)]
public void ControllerCreated_InvokedInUnitTests(string uri, string content) public void ControllerCreated_InvokedInUnitTests(string uri, string content)
{ {
// Arrange // Arrange
@ -105,7 +105,7 @@ namespace Microsoft.AspNet.Mvc
[Theory] [Theory]
[InlineData("<html>CreatedBody</html>", "text/html", "Created.html")] [InlineData("<html>CreatedBody</html>", "text/html", "Created.html")]
[InlineData(null, null, null)] [InlineData("<html>CreatedBody</html>", null, null)]
public void ControllerFileContent_InvokedInUnitTests(string content, string contentType, string fileName) public void ControllerFileContent_InvokedInUnitTests(string content, string contentType, string fileName)
{ {
// Arrange // Arrange
@ -133,7 +133,7 @@ namespace Microsoft.AspNet.Mvc
[Theory] [Theory]
[InlineData("<html>CreatedBody</html>", "text/html", "Created.html")] [InlineData("<html>CreatedBody</html>", "text/html", "Created.html")]
[InlineData(null, null, null)] [InlineData("<html>CreatedBody</html>", null, null)]
public void ControllerFileStream_InvokedInUnitTests(string content, string contentType, string fileName) public void ControllerFileStream_InvokedInUnitTests(string content, string contentType, string fileName)
{ {
// Arrange // Arrange
@ -485,14 +485,13 @@ namespace Microsoft.AspNet.Mvc
public IActionResult FileContent_Action(string content, string contentType, string fileName) public IActionResult FileContent_Action(string content, string contentType, string fileName)
{ {
var contentArray = string.IsNullOrEmpty(content) ? null : Encoding.UTF8.GetBytes(content); var contentArray = Encoding.UTF8.GetBytes(content);
return File(contentArray, contentType, fileName); return File(contentArray, contentType, fileName);
} }
public IActionResult FileStream_Action(string content, string contentType, string fileName) public IActionResult FileStream_Action(string content, string contentType, string fileName)
{ {
var memoryStream = string.IsNullOrEmpty(content) ? null : var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(content));
new MemoryStream(Encoding.UTF8.GetBytes(content));
return File(memoryStream, contentType, fileName); return File(memoryStream, contentType, fileName);
} }