diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/RedirectToPageResultExecutor.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/RedirectToPageResultExecutor.cs index 8a7494cdb9..8ff19456af 100644 --- a/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/RedirectToPageResultExecutor.cs +++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/RedirectToPageResultExecutor.cs @@ -2,8 +2,10 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Routing; using Microsoft.Extensions.Logging; +using Microsoft.Net.Http.Headers; namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal { @@ -44,7 +46,17 @@ public void Execute(ActionContext context, RedirectToPageResult result) } _logger.RedirectToPageResultExecuting(result.PageName); - context.HttpContext.Response.Redirect(destinationUrl, result.Permanent); + + if (result.PreserveMethod) + { + context.HttpContext.Response.StatusCode = result.Permanent ? + StatusCodes.Status308PermanentRedirect : StatusCodes.Status307TemporaryRedirect; + context.HttpContext.Response.Headers[HeaderNames.Location] = destinationUrl; + } + else + { + context.HttpContext.Response.Redirect(destinationUrl, result.Permanent); + } } } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Page.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/Page.cs index 23d90aa12d..3ad708a7b1 100644 --- a/src/Microsoft.AspNetCore.Mvc.RazorPages/Page.cs +++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/Page.cs @@ -1008,6 +1008,50 @@ protected RedirectToPageResult RedirectToPagePermanent(string pageName, string f protected RedirectToPageResult RedirectToPagePermanent(string pageName, object routeValues, string fragment) => new RedirectToPageResult(pageName, routeValues, permanent: true, fragment: fragment); + /// + /// Redirects () to the specified page with + /// set to false and + /// set to true, using the specified , , and . + /// + /// The name of the page. + /// The route data to use for generating the URL. + /// The fragment to add to the URL. + /// The created for the response. + public virtual RedirectToPageResult RedirectToPagePreserveMethod( + string pageName = null, + object routeValues = null, + string fragment = null) + { + return new RedirectToPageResult( + pageName: pageName, + routeValues: routeValues, + permanent: false, + preserveMethod: true, + fragment: fragment); + } + + /// + /// Redirects () to the specified route with + /// set to true and + /// set to true, using the specified , , and . + /// + /// The name of the page. + /// The route data to use for generating the URL. + /// The fragment to add to the URL. + /// The created for the response. + public virtual RedirectToPageResult RedirectToPagePermanentPreserveMethod( + string pageName = null, + object routeValues = null, + string fragment = null) + { + return new RedirectToPageResult( + pageName: pageName, + routeValues: routeValues, + permanent: true, + preserveMethod: true, + fragment: fragment); + } + /// /// Creates a with the specified authentication scheme. /// diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/PageModel.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/PageModel.cs index afbc007b9e..dc8ab8b044 100644 --- a/src/Microsoft.AspNetCore.Mvc.RazorPages/PageModel.cs +++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/PageModel.cs @@ -513,7 +513,14 @@ public virtual PhysicalFileResult PhysicalFile(string physicalPath, string conte /// The URL to redirect to. /// The created for the response. protected internal RedirectResult Redirect(string url) - => new RedirectResult(url); + { + if (string.IsNullOrEmpty(url)) + { + throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(url)); + } + + return new RedirectResult(url); + } /// /// Creates a object with set to true @@ -1046,6 +1053,50 @@ protected internal RedirectToPageResult RedirectToPagePermanent(string pageName, protected internal RedirectToPageResult RedirectToPagePermanent(string pageName, object routeValues, string fragment) => new RedirectToPageResult(pageName, routeValues, permanent: true, fragment: fragment); + /// + /// Redirects () to the specified page with + /// set to false and + /// set to true, using the specified , , and . + /// + /// The name of the page. + /// The route data to use for generating the URL. + /// The fragment to add to the URL. + /// The created for the response. + public virtual RedirectToPageResult RedirectToPagePreserveMethod( + string pageName = null, + object routeValues = null, + string fragment = null) + { + return new RedirectToPageResult( + pageName: pageName, + routeValues: routeValues, + permanent: false, + preserveMethod: true, + fragment: fragment); + } + + /// + /// Redirects () to the specified route with + /// set to true and + /// set to true, using the specified , , and . + /// + /// The name of the page. + /// The route data to use for generating the URL. + /// The fragment to add to the URL. + /// The created for the response. + public virtual RedirectToPageResult RedirectToPagePermanentPreserveMethod( + string pageName = null, + object routeValues = null, + string fragment = null) + { + return new RedirectToPageResult( + pageName: pageName, + routeValues: routeValues, + permanent: true, + preserveMethod: true, + fragment: fragment); + } + /// /// Creates a with the specified authentication scheme. /// diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/RedirectToPageResult.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/RedirectToPageResult.cs index d808d1f44b..fcb29e81b5 100644 --- a/src/Microsoft.AspNetCore.Mvc.RazorPages/RedirectToPageResult.cs +++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/RedirectToPageResult.cs @@ -41,8 +41,8 @@ public RedirectToPageResult(string pageName, object routeValues) /// Initializes a new instance of the with the values /// provided. /// - /// The name of the route. - /// The parameters for the route. + /// The name of the page. + /// The parameters for the page. /// If set to true, makes the redirect permanent (301). Otherwise a temporary redirect is used (302). public RedirectToPageResult( string pageName, @@ -52,11 +52,27 @@ public RedirectToPageResult(string pageName, object routeValues) { } + /// + /// Initializes a new instance of the with the values provided. + /// + /// The name of the page. + /// The parameters for the page. + /// If set to true, makes the redirect permanent (301). Otherwise a temporary redirect is used (302). + /// If set to true, make the temporary redirect (307) or permanent redirect (308) preserve the intial request method. + public RedirectToPageResult( + string pageName, + object routeValues, + bool permanent, + bool preserveMethod) + : this(pageName, routeValues, permanent, preserveMethod, fragment: null) + { + } + /// /// Initializes a new instance of the with the values /// provided. /// - /// The name of the route. + /// The name of the page. /// The parameters for the route. /// The fragment to add to the URL. public RedirectToPageResult( @@ -71,8 +87,8 @@ public RedirectToPageResult(string pageName, object routeValues) /// Initializes a new instance of the with the values /// provided. /// - /// The name of the route. - /// The parameters for the route. + /// The name of the page. + /// The parameters for the page. /// If set to true, makes the redirect permanent (301). Otherwise a temporary redirect is used (302). /// The fragment to add to the URL. public RedirectToPageResult( @@ -87,6 +103,29 @@ public RedirectToPageResult(string pageName, object routeValues) Fragment = fragment; } + /// + /// Initializes a new instance of the with the values + /// provided. + /// + /// The name of the page. + /// The parameters for the page. + /// If set to true, makes the redirect permanent (301). Otherwise a temporary redirect is used (302). + /// If set to true, make the temporary redirect (307) or permanent redirect (308) preserve the intial request method. + /// The fragment to add to the URL. + public RedirectToPageResult( + string pageName, + object routeValues, + bool permanent, + bool preserveMethod, + string fragment) + { + PageName = pageName; + RouteValues = routeValues == null ? null : new RouteValueDictionary(routeValues); + PreserveMethod = preserveMethod; + Permanent = permanent; + Fragment = fragment; + } + /// /// Gets or sets the used to generate URLs. /// @@ -107,6 +146,11 @@ public RedirectToPageResult(string pageName, object routeValues) /// public bool Permanent { get; set; } + /// + /// Gets or sets an indication that the redirect preserves the initial request method. + /// + public bool PreserveMethod { get; set; } + /// /// Gets or sets the fragment to add to the URL. /// diff --git a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/PageModelTest.cs b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/PageModelTest.cs index 87509ff87c..e5019878b8 100644 --- a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/PageModelTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/PageModelTest.cs @@ -937,6 +937,65 @@ public static IEnumerable RedirectTestData } } + [Fact] + public void RedirectToPagePreserveMethod_WithParameterUrl_SetsRedirectResultPreserveMethod() + { + // Arrange + var pageModel = new TestPageModel(); + var url = "/test/url"; + + // Act + var result = pageModel.RedirectToPagePreserveMethod(url); + + // Assert + Assert.IsType(result); + Assert.True(result.PreserveMethod); + Assert.False(result.Permanent); + Assert.Same(url, result.PageName); + } + + [Theory] + [MemberData(nameof(RedirectTestData))] + public void RedirectToPagePreserveMethod_SetsResultProperties( + object routeValues, + IEnumerable> expected) + { + // Arrange + var pageModel = new TestPageModel(); + var pageName = "CustomRouteName"; + + // Act + var resultPermanent = pageModel.RedirectToPagePreserveMethod(pageName, routeValues); + + // Assert + Assert.IsType(resultPermanent); + Assert.True(resultPermanent.PreserveMethod); + Assert.False(resultPermanent.Permanent); + Assert.Same(pageName, resultPermanent.PageName); + Assert.Equal(expected, resultPermanent.RouteValues); + } + + [Theory] + [MemberData(nameof(RedirectTestData))] + public void RedirectToPagePermanentPreserveMethod_SetsResultProperties( + object routeValues, + IEnumerable> expected) + { + // Arrange + var pageModel = new TestPageModel(); + var routeName = "CustomRouteName"; + + // Act + var resultPermanent = pageModel.RedirectToPagePermanentPreserveMethod(routeName, routeValues); + + // Assert + Assert.IsType(resultPermanent); + Assert.True(resultPermanent.PreserveMethod); + Assert.True(resultPermanent.Permanent); + Assert.Same(routeName, resultPermanent.PageName); + Assert.Equal(expected, resultPermanent.RouteValues); + } + [Theory] [MemberData(nameof(RedirectTestData))] public void RedirectToRoute_WithParameterRouteNameAndRouteValues_SetsResultSameRouteNameAndRouteValues( @@ -1020,6 +1079,7 @@ public static IEnumerable RedirectTestData Assert.Same(routeName, resultPermanent.RouteName); Assert.Equal(expected, resultPermanent.RouteValues); } + [Fact] public void File_WithContents() { diff --git a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/PageTest.cs b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/PageTest.cs index 83d8a559b8..4a355fb289 100644 --- a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/PageTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/PageTest.cs @@ -1014,6 +1014,65 @@ public void RedirectToRoutePermanentPreserveMethod_WithParameterRouteName_SetsRe Assert.Equal(expected, resultPermanent.RouteValues); } + [Fact] + public void RedirectToPagePreserveMethod_WithParameterUrl_SetsRedirectResultPreserveMethod() + { + // Arrange + var pageModel = new TestPage(); + var url = "/test/url"; + + // Act + var result = pageModel.RedirectToPagePreserveMethod(url); + + // Assert + Assert.IsType(result); + Assert.True(result.PreserveMethod); + Assert.False(result.Permanent); + Assert.Same(url, result.PageName); + } + + [Theory] + [MemberData(nameof(RedirectTestData))] + public void RedirectToPagePreserveMethod_SetsResultProperties( + object routeValues, + IEnumerable> expected) + { + // Arrange + var pageModel = new TestPage(); + var pageName = "CustomRouteName"; + + // Act + var resultPermanent = pageModel.RedirectToPagePreserveMethod(pageName, routeValues); + + // Assert + Assert.IsType(resultPermanent); + Assert.True(resultPermanent.PreserveMethod); + Assert.False(resultPermanent.Permanent); + Assert.Same(pageName, resultPermanent.PageName); + Assert.Equal(expected, resultPermanent.RouteValues); + } + + [Theory] + [MemberData(nameof(RedirectTestData))] + public void RedirectToPagePermanentPreserveMethod_SetsResultProperties( + object routeValues, + IEnumerable> expected) + { + // Arrange + var pageModel = new TestPage(); + var routeName = "CustomRouteName"; + + // Act + var resultPermanent = pageModel.RedirectToPagePermanentPreserveMethod(routeName, routeValues); + + // Assert + Assert.IsType(resultPermanent); + Assert.True(resultPermanent.PreserveMethod); + Assert.True(resultPermanent.Permanent); + Assert.Same(routeName, resultPermanent.PageName); + Assert.Equal(expected, resultPermanent.RouteValues); + } + [Fact] public void File_WithContents() {