WebFX 156: Adding support for RedirectToAction and RedirectToRoute

This commit is contained in:
Sornakumar 2014-04-08 10:30:24 -07:00 committed by sornaks
parent 1536daa107
commit ecd8ddeae4
8 changed files with 502 additions and 1 deletions

View File

@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.Core;
namespace Microsoft.AspNet.Mvc
{
public class RedirectToActionResult : IActionResult
{
public RedirectToActionResult([NotNull] IUrlHelper urlHelper, string actionName,
string controllerName, IDictionary<string, object> routeValues)
: this(urlHelper, actionName, controllerName, routeValues, permanent: false)
{
}
public RedirectToActionResult([NotNull] IUrlHelper urlHelper, string actionName,
string controllerName, IDictionary<string, object> routeValues, bool permanent)
{
UrlHelper = urlHelper;
ActionName = actionName;
ControllerName = controllerName;
RouteValues = routeValues;
Permanent = permanent;
}
public IUrlHelper UrlHelper { get; private set; }
public string ActionName { get; private set; }
public string ControllerName { get; private set; }
public IDictionary<string, object> RouteValues { get; private set; }
public bool Permanent { get; private set; }
public async Task ExecuteResultAsync([NotNull] ActionContext context)
{
var destinationUrl = UrlHelper.Action(ActionName, ControllerName, RouteValues);
if (string.IsNullOrEmpty(destinationUrl))
{
throw new InvalidOperationException(Resources.NoRoutesMatched);
}
context.HttpContext.Response.Redirect(destinationUrl, Permanent);
}
}
}

View File

@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.Core;
namespace Microsoft.AspNet.Mvc
{
public class RedirectToRouteResult : IActionResult
{
public RedirectToRouteResult([NotNull] IUrlHelper urlHelper, IDictionary<string, object> routeValues)
: this(urlHelper, routeValues, permanent: false)
{
}
public RedirectToRouteResult([NotNull] IUrlHelper urlHelper,
IDictionary<string, object> routeValues, bool permanent)
{
UrlHelper = urlHelper;
RouteValues = routeValues;
Permanent = permanent;
}
public IUrlHelper UrlHelper { get; private set; }
public IDictionary<string, object> RouteValues { get; private set; }
public bool Permanent { get; private set; }
public async Task ExecuteResultAsync([NotNull] ActionContext context)
{
var destinationUrl = UrlHelper.RouteUrl(RouteValues);
if (string.IsNullOrEmpty(destinationUrl))
{
throw new InvalidOperationException(Resources.NoRoutesMatched);
}
context.HttpContext.Response.Redirect(destinationUrl, Permanent);
}
}
}

View File

@ -119,5 +119,59 @@ namespace Microsoft.AspNet.Mvc
return new RedirectResult(url, permanent: true);
}
public RedirectToActionResult RedirectToAction(string actionName)
{
return RedirectToAction(actionName, routeValues: null);
}
public RedirectToActionResult RedirectToAction(string actionName, object routeValues)
{
return RedirectToAction(actionName, controllerName: null, routeValues: routeValues);
}
public RedirectToActionResult RedirectToAction(string actionName, string controllerName)
{
return RedirectToAction(actionName, controllerName, routeValues: null);
}
public RedirectToActionResult RedirectToAction(string actionName, string controllerName,
object routeValues)
{
return new RedirectToActionResult(Url, actionName, controllerName,
TypeHelper.ObjectToDictionary(routeValues));
}
public RedirectToActionResult RedirectToActionPermanent(string actionName)
{
return RedirectToActionPermanent(actionName, routeValues: null);
}
public RedirectToActionResult RedirectToActionPermanent(string actionName, object routeValues)
{
return RedirectToActionPermanent(actionName, controllerName: null, routeValues: routeValues);
}
public RedirectToActionResult RedirectToActionPermanent(string actionName, string controllerName)
{
return RedirectToActionPermanent(actionName, controllerName, routeValues: null);
}
public RedirectToActionResult RedirectToActionPermanent(string actionName, string controllerName,
object routeValues)
{
return new RedirectToActionResult(Url, actionName, controllerName,
TypeHelper.ObjectToDictionary(routeValues), permanent: true);
}
public RedirectToRouteResult RedirectToRoute(object routeValues)
{
return new RedirectToRouteResult(Url, TypeHelper.ObjectToDictionary(routeValues));
}
public RedirectToRouteResult RedirectToRoutePermanent(object routeValues)
{
return new RedirectToRouteResult(Url, TypeHelper.ObjectToDictionary(routeValues), permanent: true);
}
}
}

View File

@ -538,6 +538,22 @@ namespace Microsoft.AspNet.Mvc.Core
return string.Format(CultureInfo.CurrentCulture, GetString("ViewData_WrongTModelType"), p0, p1);
}
/// <summary>
/// No route matches the supplied values.
/// </summary>
internal static string NoRoutesMatched
{
get { return GetString("NoRoutesMatched"); }
}
/// <summary>
/// No route matches the supplied values.
/// </summary>
internal static string FormatNoRoutesMatched()
{
return GetString("NoRoutesMatched");
}
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);

View File

@ -216,4 +216,7 @@
<data name="ViewData_WrongTModelType" xml:space="preserve">
<value>The model item passed into the ViewDataDictionary is of type '{0}', but this ViewDataDictionary instance requires a model item of type '{1}'.</value>
</data>
<data name="NoRoutesMatched" xml:space="preserve">
<value>No route matches the supplied values.</value>
</data>
</root>

View File

@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using Microsoft.AspNet.Abstractions;
using Microsoft.AspNet.Routing;
using Microsoft.AspNet.Testing;
using Moq;
using Xunit;
namespace Microsoft.AspNet.Mvc.Core.Test.ActionResults
{
public class RedirectToActionResultTest
{
[Fact]
public async void RedirectToAction_Execute_PassesCorrectValuesToRedirect()
{
// Arrange
var expectedUrl = "SampleAction";
var expectedPermanentFlag = false;
var httpContext = new Mock<HttpContext>();
var httpResponse = new Mock<HttpResponse>();
httpContext.Setup(o => o.Response).Returns(httpResponse.Object);
var actionContext = new ActionContext(httpContext.Object,
Mock.Of<IRouter>(),
new Dictionary<string, object>(),
new ActionDescriptor());
IUrlHelper urlHelper = GetMockUrlHelper(expectedUrl);
RedirectToActionResult result = new RedirectToActionResult(urlHelper, "SampleAction", null, null);
// Act
await result.ExecuteResultAsync(actionContext);
// Assert
// Verifying if Redirect was called with the specific Url and parameter flag.
// Thus we verify that the Url returned by UrlHelper is passed properly to
// Redirect method and that the method is called exactly once.
httpResponse.Verify(r => r.Redirect(expectedUrl, expectedPermanentFlag), Times.Exactly(1));
}
[Fact]
public void RedirectToAction_Execute_ThrowsOnNullUrl()
{
// Arrange
var httpContext = new Mock<HttpContext>();
httpContext.Setup(o => o.Response).Returns(new Mock<HttpResponse>().Object);
var actionContext = new ActionContext(httpContext.Object,
Mock.Of<IRouter>(),
new Dictionary<string, object>(),
new ActionDescriptor());
IUrlHelper urlHelper = GetMockUrlHelper(returnValue: null);
RedirectToActionResult result = new RedirectToActionResult(urlHelper, null, null, null);
// Act & Assert
ExceptionAssert.ThrowsAsync<InvalidOperationException>(
async () =>
{
await result.ExecuteResultAsync(actionContext);
},
"No route matches the supplied values.");
}
private static IUrlHelper GetMockUrlHelper(string returnValue)
{
var urlHelper = new Mock<IUrlHelper>();
urlHelper.Setup(o => o.Action(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<object>(),
It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>())).Returns(returnValue);
return urlHelper.Object;
}
}
}

View File

@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using Microsoft.AspNet.Abstractions;
using Microsoft.AspNet.Routing;
using Microsoft.AspNet.Testing;
using Moq;
using Xunit;
namespace Microsoft.AspNet.Mvc.Core
{
public class RedirectToRouteResultTest
{
[Theory]
[MemberData("RedirectToRouteData")]
public async void RedirectToRoute_Execute_PassesCorrectValuesToRedirect(object values)
{
// Arrange
var expectedUrl = "SampleAction";
var expectedPermanentFlag = false;
var httpContext = new Mock<HttpContext>();
var httpResponse = new Mock<HttpResponse>();
httpContext.Setup(o => o.Response).Returns(httpResponse.Object);
var actionContext = new ActionContext(httpContext.Object,
Mock.Of<IRouter>(),
new Dictionary<string, object>(),
new ActionDescriptor());
IUrlHelper urlHelper = GetMockUrlHelper(expectedUrl);
RedirectToRouteResult result = new RedirectToRouteResult(urlHelper, TypeHelper.ObjectToDictionary(values));
// Act
await result.ExecuteResultAsync(actionContext);
// Assert
// Verifying if Redirect was called with the specific Url and parameter flag.
// Thus we verify that the Url returned by UrlHelper is passed properly to
// Redirect method and that the method is called exactly once.
httpResponse.Verify(r => r.Redirect(expectedUrl, expectedPermanentFlag), Times.Exactly(1));
}
[Fact]
public void RedirectToRoute_Execute_ThrowsOnNullUrl()
{
// Arrange
var httpContext = new Mock<HttpContext>();
httpContext.Setup(o => o.Response).Returns(new Mock<HttpResponse>().Object);
var actionContext = new ActionContext(httpContext.Object,
Mock.Of<IRouter>(),
new Dictionary<string, object>(),
new ActionDescriptor());
IUrlHelper urlHelper = GetMockUrlHelper(returnValue: null);
RedirectToRouteResult result = new RedirectToRouteResult(urlHelper, new Dictionary<string, object>());
// Act & Assert
ExceptionAssert.ThrowsAsync<InvalidOperationException>(
async () =>
{
await result.ExecuteResultAsync(actionContext);
},
"No route matches the supplied values.");
}
public static IEnumerable<object[]> RedirectToRouteData
{
get
{
yield return new object[] { null };
yield return
new object[] {
new Dictionary<string, string>() { { "hello", "world" } }
};
yield return
new object[] {
new RouteValueDictionary(new Dictionary<string, string>() {
{ "test", "case" }, { "sample", "route" } })
};
}
}
private static IUrlHelper GetMockUrlHelper(string returnValue)
{
var urlHelper = new Mock<IUrlHelper>();
urlHelper.Setup(o => o.RouteUrl(It.IsAny<object>(), It.IsAny<string>(),
It.IsAny<string>(), It.IsAny<string>())).Returns(returnValue);
return urlHelper.Object;
}
}
}

View File

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Routing;
using Microsoft.AspNet.Testing;
using Xunit;
@ -83,5 +84,182 @@ namespace Microsoft.AspNet.Mvc.Core
ExceptionAssert.ThrowsArgument(
() => controller.RedirectPermanent(url: url), "url", "The value cannot be null or empty");
}
[Fact]
public void RedirectToAction_Temporary_Returns_SameAction()
{
// Arrange
var controller = new Controller();
// Act
var resultTemporary = controller.RedirectToAction("SampleAction");
// Assert
Assert.False(resultTemporary.Permanent);
Assert.Equal("SampleAction", resultTemporary.ActionName);
}
[Fact]
public void RedirectToAction_Permanent_Returns_SameAction()
{
// Arrange
var controller = new Controller();
// Act
var resultPermanent = controller.RedirectToActionPermanent("SampleAction");
// Assert
Assert.True(resultPermanent.Permanent);
Assert.Equal("SampleAction", resultPermanent.ActionName);
}
[Theory]
[InlineData("")]
[InlineData(null)]
[InlineData("SampleController")]
public void RedirectToAction_Temporary_Returns_SameController(string controllerName)
{
// Arrange
var controller = new Controller();
// Act
var resultTemporary = controller.RedirectToAction("SampleAction", controllerName);
// Assert
Assert.False(resultTemporary.Permanent);
Assert.Equal("SampleAction", resultTemporary.ActionName);
Assert.Equal(controllerName, resultTemporary.ControllerName);
}
[Theory]
[InlineData("")]
[InlineData(null)]
[InlineData("SampleController")]
public void RedirectToAction_Permanent_Returns_SameController(string controllerName)
{
// Arrange
var controller = new Controller();
// Act
var resultPermanent = controller.RedirectToActionPermanent("SampleAction", controllerName);
// Assert
Assert.True(resultPermanent.Permanent);
Assert.Equal("SampleAction", resultPermanent.ActionName);
Assert.Equal(controllerName, resultPermanent.ControllerName);
}
[Theory]
[MemberData("RedirectTestData")]
public void RedirectToAction_Temporary_Returns_SameActionControllerAndRouteValues(object routeValues)
{
// Arrange
var controller = new Controller();
// Act
var resultTemporary = controller.RedirectToAction("SampleAction", "SampleController", routeValues);
// Assert
Assert.False(resultTemporary.Permanent);
Assert.Equal("SampleAction", resultTemporary.ActionName);
Assert.Equal("SampleController", resultTemporary.ControllerName);
Assert.Equal(TypeHelper.ObjectToDictionary(routeValues), resultTemporary.RouteValues);
}
[Theory]
[MemberData("RedirectTestData")]
public void RedirectToAction_Permanent_Returns_SameActionControllerAndRouteValues(object routeValues)
{
// Arrange
var controller = new Controller();
// Act
var resultPermanent = controller.RedirectToActionPermanent("SampleAction", "SampleController", routeValues);
// Assert
Assert.True(resultPermanent.Permanent);
Assert.Equal("SampleAction", resultPermanent.ActionName);
Assert.Equal("SampleController", resultPermanent.ControllerName);
Assert.Equal(TypeHelper.ObjectToDictionary(routeValues), resultPermanent.RouteValues);
}
[Theory]
[MemberData("RedirectTestData")]
public void RedirectToAction_Temporary_Returns_SameActionAndRouteValues(object routeValues)
{
// Arrange
var controller = new Controller();
// Act
var resultTemporary = controller.RedirectToAction(actionName: null, routeValues: routeValues);
// Assert
Assert.False(resultTemporary.Permanent);
Assert.Null(resultTemporary.ActionName);
Assert.Equal(TypeHelper.ObjectToDictionary(routeValues), resultTemporary.RouteValues);
}
[Theory]
[MemberData("RedirectTestData")]
public void RedirectToAction_Permanent_Returns_SameActionAndRouteValues(object routeValues)
{
// Arrange
var controller = new Controller();
// Act
var resultPermanent = controller.RedirectToActionPermanent(null, routeValues);
// Assert
Assert.True(resultPermanent.Permanent);
Assert.Null(resultPermanent.ActionName);
Assert.Equal(TypeHelper.ObjectToDictionary(routeValues), resultPermanent.RouteValues);
}
[Theory]
[MemberData("RedirectTestData")]
public void RedirectToRoute_Temporary_Returns_SameRouteValues(object routeValues)
{
// Arrange
var controller = new Controller();
// Act
var resultTemporary = controller.RedirectToRoute(routeValues);
// Assert
Assert.False(resultTemporary.Permanent);
Assert.Equal(TypeHelper.ObjectToDictionary(routeValues), resultTemporary.RouteValues);
}
[Theory]
[MemberData("RedirectTestData")]
public void RedirectToRoute_Permanent_Returns_SameRouteValues(object routeValues)
{
// Arrange
var controller = new Controller();
// Act
var resultPermanent = controller.RedirectToRoutePermanent(routeValues);
// Assert
Assert.True(resultPermanent.Permanent);
Assert.Equal(TypeHelper.ObjectToDictionary(routeValues), resultPermanent.RouteValues);
}
public static IEnumerable<object[]> RedirectTestData
{
get
{
yield return new object[] { null };
yield return
new object[] {
new Dictionary<string, string>() { { "hello", "world" } }
};
yield return
new object[] {
new RouteValueDictionary(new Dictionary<string, string>() {
{ "test", "case" }, { "sample", "route" } })
};
}
}
}
}