Skip to content
This repository has been archived by the owner on Dec 14, 2018. It is now read-only.

Commit

Permalink
RedirectToPage from a form-action handler includes the formaction
Browse files Browse the repository at this point in the history
Fixes #6104
  • Loading branch information
pranavkm committed Apr 12, 2017
1 parent 106b348 commit ec950ac
Show file tree
Hide file tree
Showing 5 changed files with 224 additions and 14 deletions.
Expand Up @@ -89,9 +89,9 @@ public static string Page(this IUrlHelper urlHelper, string pageName)
}

var routeValues = new RouteValueDictionary(values);
var ambientValues = urlHelper.ActionContext.RouteData.Values;
if (pageName == null)
{
var ambientValues = urlHelper.ActionContext.RouteData.Values;
if (!routeValues.ContainsKey("page") &&
ambientValues.TryGetValue("page", out var value))
{
Expand All @@ -103,6 +103,13 @@ public static string Page(this IUrlHelper urlHelper, string pageName)
routeValues["page"] = pageName;
}

if (!routeValues.ContainsKey("formaction") &&
ambientValues.TryGetValue("formaction", out var formaction))
{
// Clear out formaction unless it's explicitly specified in the routeValues.
routeValues["formaction"] = null;
}

return urlHelper.RouteUrl(
routeName: null,
values: routeValues,
Expand Down
28 changes: 28 additions & 0 deletions test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorPagesTest.cs
Expand Up @@ -763,6 +763,34 @@ public async Task RedirectToSelfWorks()
Assert.Equal(expected, response.Headers.Location.ToString());
}

[Fact]
public async Task RedirectDoesNotIncludeFormActionByDefault()
{
// Arrange
var expected = "/Pages/Redirects/RedirectFromFormActionHandler";

// Act
var response = await Client.GetAsync("/Pages/Redirects/RedirectFromFormActionHandler/RedirectToPage/10");

// Assert
Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
Assert.Equal(expected, response.Headers.Location.ToString());
}

[Fact]
public async Task RedirectToOtherHandlersWorks()
{
// Arrange
var expected = "/Pages/Redirects/RedirectFromFormActionHandler/RedirectToPage/11";

// Act
var response = await Client.GetAsync("/Pages/Redirects/RedirectFromFormActionHandler/RedirectToAnotherHandler/11");

// Assert
Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
Assert.Equal(expected, response.Headers.Location.ToString());
}

private async Task AddAntiforgeryHeaders(HttpRequestMessage request)
{
var getResponse = await Client.GetAsync(request.RequestUri);
Expand Down
Expand Up @@ -27,9 +27,7 @@ public void Page_WithName_Works()
{
RouteData = routeData,
};
var urlHelper = new Mock<IUrlHelper>();
urlHelper.SetupGet(h => h.ActionContext)
.Returns(actionContext);
var urlHelper = CreateUrlHelper(actionContext);
urlHelper.Setup(h => h.RouteUrl(It.IsAny<UrlRouteContext>()))
.Callback((UrlRouteContext context) => actual = context);

Expand Down Expand Up @@ -77,7 +75,7 @@ public void Page_WithNameAndRouteValues_Works(object values)
{
// Arrange
UrlRouteContext actual = null;
var urlHelper = new Mock<IUrlHelper>();
var urlHelper = CreateUrlHelper();
urlHelper.Setup(h => h.RouteUrl(It.IsAny<UrlRouteContext>()))
.Callback((UrlRouteContext context) => actual = context);

Expand Down Expand Up @@ -109,7 +107,7 @@ public void Page_WithNameRouteValuesAndProtocol_Works()
{
// Arrange
UrlRouteContext actual = null;
var urlHelper = new Mock<IUrlHelper>();
var urlHelper = CreateUrlHelper();
urlHelper.Setup(h => h.RouteUrl(It.IsAny<UrlRouteContext>()))
.Callback((UrlRouteContext context) => actual = context);

Expand Down Expand Up @@ -141,7 +139,7 @@ public void Page_WithNameRouteValuesProtocolAndHost_Works()
{
// Arrange
UrlRouteContext actual = null;
var urlHelper = new Mock<IUrlHelper>();
var urlHelper = CreateUrlHelper();
urlHelper.Setup(h => h.RouteUrl(It.IsAny<UrlRouteContext>()))
.Callback((UrlRouteContext context) => actual = context);

Expand Down Expand Up @@ -173,7 +171,7 @@ public void Page_WithNameRouteValuesProtocolHostAndFragment_Works()
{
// Arrange
UrlRouteContext actual = null;
var urlHelper = new Mock<IUrlHelper>();
var urlHelper = CreateUrlHelper();
urlHelper.Setup(h => h.RouteUrl(It.IsAny<UrlRouteContext>()))
.Callback((UrlRouteContext context) => actual = context);

Expand Down Expand Up @@ -217,9 +215,7 @@ public void Page_UsesAmbientRouteValue_WhenPageIsNull()
RouteData = routeData,
};

var urlHelper = new Mock<IUrlHelper>();
urlHelper.SetupGet(p => p.ActionContext)
.Returns(actionContext);
var urlHelper = CreateUrlHelper(actionContext);
urlHelper.Setup(h => h.RouteUrl(It.IsAny<UrlRouteContext>()))
.Callback((UrlRouteContext context) => actual = context);

Expand All @@ -246,5 +242,112 @@ public void Page_UsesAmbientRouteValue_WhenPageIsNull()
Assert.Equal("mytesthost", actual.Host);
Assert.Equal("#toc", actual.Fragment);
}

[Fact]
public void Page_SetsFormActionToNull_IfValueIsNotSpecifiedInRouteValues()
{
// Arrange
UrlRouteContext actual = null;
var routeData = new RouteData
{
Values =
{
{ "page", "ambient-page" },
{ "formaction", "ambient-formaction" },
}
};
var actionContext = new ActionContext
{
RouteData = routeData,
};

var urlHelper = CreateUrlHelper(actionContext);
urlHelper.Setup(h => h.RouteUrl(It.IsAny<UrlRouteContext>()))
.Callback((UrlRouteContext context) => actual = context);

// Act
string page = null;
urlHelper.Object.Page(page, new { id = 13 }, "https", "mytesthost", "#toc");

// Assert
urlHelper.Verify();
Assert.NotNull(actual);
Assert.Null(actual.RouteName);
Assert.Collection(Assert.IsType<RouteValueDictionary>(actual.Values),
value =>
{
Assert.Equal("id", value.Key);
Assert.Equal(13, value.Value);
},
value =>
{
Assert.Equal("page", value.Key);
Assert.Equal("ambient-page", value.Value);
},
value =>
{
Assert.Equal("formaction", value.Key);
Assert.Null(value.Value);
});
}

[Fact]
public void Page_UsesExplicitlySpecifiedFormActionValue()
{
// Arrange
UrlRouteContext actual = null;
var routeData = new RouteData
{
Values =
{
{ "page", "ambient-page" },
{ "formaction", "ambient-formaction" },
}
};
var actionContext = new ActionContext
{
RouteData = routeData,
};

var urlHelper = CreateUrlHelper(actionContext);
urlHelper.Setup(h => h.RouteUrl(It.IsAny<UrlRouteContext>()))
.Callback((UrlRouteContext context) => actual = context);

// Act
string page = null;
urlHelper.Object.Page(page, new { formaction = "exact-formaction" }, "https", "mytesthost", "#toc");

// Assert
urlHelper.Verify();
Assert.NotNull(actual);
Assert.Null(actual.RouteName);
Assert.Collection(Assert.IsType<RouteValueDictionary>(actual.Values),
value =>
{
Assert.Equal("formaction", value.Key);
Assert.Equal("exact-formaction", value.Value);
},
value =>
{
Assert.Equal("page", value.Key);
Assert.Equal("ambient-page", value.Value);
});
}

private static Mock<IUrlHelper> CreateUrlHelper(ActionContext context = null)
{
if (context == null)
{
context = new ActionContext
{
RouteData = new RouteData(),
};
}

var urlHelper = new Mock<IUrlHelper>();
urlHelper.SetupGet(h => h.ActionContext)
.Returns(context);
return urlHelper;
}
}
}
Expand Up @@ -32,7 +32,7 @@ public async Task ExecuteResultAsync_ThrowsOnNullUrl()

var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());

var urlHelper = GetUrlHelper(returnValue: null);
var urlHelper = GetUrlHelper(actionContext, returnValue: null);
var result = new RedirectToPageResult("some-page", new Dictionary<string, object>())
{
UrlHelper = urlHelper,
Expand Down Expand Up @@ -64,7 +64,7 @@ public async Task ExecuteResultAsync_PassesCorrectValuesToRedirect(bool permanen
new RouteData(),
new ActionDescriptor());

var urlHelper = GetUrlHelper(expectedUrl);
var urlHelper = GetUrlHelper(actionContext, expectedUrl);
var result = new RedirectToPageResult("MyPage", new { id = 10, test = "value" }, permanentRedirect)
{
UrlHelper = urlHelper,
Expand All @@ -89,10 +89,12 @@ public async Task ExecuteResultAsync_WithAllParameters()
var pageContext = new PageContext
{
HttpContext = httpContext,
RouteData = new RouteData(),
};

UrlRouteContext context = null;
var urlHelper = new Mock<IUrlHelper>();
urlHelper.SetupGet(h => h.ActionContext).Returns(pageContext);
urlHelper.Setup(h => h.RouteUrl(It.IsAny<UrlRouteContext>()))
.Callback((UrlRouteContext c) => context = c)
.Returns("some-value");
Expand Down Expand Up @@ -174,6 +176,62 @@ public async Task RedirectToPage_WithNullPage_UsesAmbientValue()
});
}

[Fact]
public async Task RedirectToPage_DoesNotUseAmbientFormAction()
{
// Arrange
var expected = "path/to/this-page";
var httpContext = new Mock<HttpContext>();
var httpResponse = new Mock<HttpResponse>();
httpContext.SetupGet(c => c.Response)
.Returns(httpResponse.Object);
httpContext.SetupGet(c => c.RequestServices)
.Returns(CreateServices());
var routeData = new RouteData
{
Values =
{
["page"] = expected,
["formaction"] = "delete",
}
};

var actionContext = new ActionContext(
httpContext.Object,
routeData,
new ActionDescriptor());

UrlRouteContext context = null;
var urlHelper = new Mock<IUrlHelper>();
urlHelper.Setup(h => h.RouteUrl(It.IsAny<UrlRouteContext>()))
.Callback((UrlRouteContext c) => context = c)
.Returns("some-value");
urlHelper.SetupGet(h => h.ActionContext)
.Returns(actionContext);
var pageName = (string)null;
var result = new RedirectToPageResult(pageName)
{
UrlHelper = urlHelper.Object,
};

// Act
await result.ExecuteResultAsync(actionContext);

// Assert
Assert.NotNull(context);
Assert.Collection(Assert.IsType<RouteValueDictionary>(context.Values),
value =>
{
Assert.Equal("page", value.Key);
Assert.Equal(expected, value.Value);
},
value =>
{
Assert.Equal("formaction", value.Key);
Assert.Null(value.Value);
});
}

private static IServiceProvider CreateServices(IUrlHelperFactory factory = null)
{
var services = new ServiceCollection();
Expand All @@ -192,9 +250,10 @@ private static IServiceProvider CreateServices(IUrlHelperFactory factory = null)
return services.BuildServiceProvider();
}

private static IUrlHelper GetUrlHelper(string returnValue)
private static IUrlHelper GetUrlHelper(ActionContext context, string returnValue)
{
var urlHelper = new Mock<IUrlHelper>();
urlHelper.SetupGet(h => h.ActionContext).Returns(context);
urlHelper.Setup(o => o.RouteUrl(It.IsAny<UrlRouteContext>())).Returns(returnValue);
return urlHelper.Object;
}
Expand Down
@@ -0,0 +1,13 @@
@page "{formaction?}/{id:int?}"
@functions
{
public IActionResult OnGetRedirectToPage(int id)
{
return RedirectToPage();
}

public IActionResult OnGetRedirectToAnotherHandler(int id)
{
return RedirectToPage(new { formaction = "RedirectToPage", id = id });
}
}

0 comments on commit ec950ac

Please sign in to comment.