From 48b436ec8a2aa2d8ebcfc8682e85d336e217c8ce Mon Sep 17 00:00:00 2001 From: Ryan Brandenburg Date: Mon, 25 Apr 2016 15:08:40 -0700 Subject: [PATCH] Allow query string on ReExecuting status code page --- .../StatusCodePagesExtensions.cs | 16 ++- .../StatusCodeReExecuteFeature.cs | 2 + .../ExceptionHandlerTest.cs | 100 ++++++++++++++++++ 3 files changed, 116 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.AspNetCore.Diagnostics/StatusCodePage/StatusCodePagesExtensions.cs b/src/Microsoft.AspNetCore.Diagnostics/StatusCodePage/StatusCodePagesExtensions.cs index dbc753399c..2245d97706 100644 --- a/src/Microsoft.AspNetCore.Diagnostics/StatusCodePage/StatusCodePagesExtensions.cs +++ b/src/Microsoft.AspNetCore.Diagnostics/StatusCodePage/StatusCodePagesExtensions.cs @@ -158,8 +158,12 @@ namespace Microsoft.AspNetCore.Builder /// /// /// + /// /// - public static IApplicationBuilder UseStatusCodePagesWithReExecute(this IApplicationBuilder app, string pathFormat) + public static IApplicationBuilder UseStatusCodePagesWithReExecute( + this IApplicationBuilder app, + string pathFormat, + string queryFormat = null) { if (app == null) { @@ -168,23 +172,31 @@ namespace Microsoft.AspNetCore.Builder return app.UseStatusCodePages(async context => { - var newPath = new PathString(string.Format(CultureInfo.InvariantCulture, pathFormat, context.HttpContext.Response.StatusCode)); + var newPath = new PathString( + string.Format(CultureInfo.InvariantCulture, pathFormat, context.HttpContext.Response.StatusCode)); + var formatedQueryString = queryFormat == null ? null : + string.Format(CultureInfo.InvariantCulture, queryFormat, context.HttpContext.Response.StatusCode); + var newQueryString = queryFormat == null ? QueryString.Empty : new QueryString(formatedQueryString); var originalPath = context.HttpContext.Request.Path; + var originalQueryString = context.HttpContext.Request.QueryString; // Store the original paths so the app can check it. context.HttpContext.Features.Set(new StatusCodeReExecuteFeature() { OriginalPathBase = context.HttpContext.Request.PathBase.Value, OriginalPath = originalPath.Value, + OriginalQueryString = originalQueryString.HasValue ? originalQueryString.Value : null, }); context.HttpContext.Request.Path = newPath; + context.HttpContext.Request.QueryString = newQueryString; try { await context.Next(context.HttpContext); } finally { + context.HttpContext.Request.QueryString = originalQueryString; context.HttpContext.Request.Path = originalPath; context.HttpContext.Features.Set(null); } diff --git a/src/Microsoft.AspNetCore.Diagnostics/StatusCodePage/StatusCodeReExecuteFeature.cs b/src/Microsoft.AspNetCore.Diagnostics/StatusCodePage/StatusCodeReExecuteFeature.cs index fb95e02396..8b25d24385 100644 --- a/src/Microsoft.AspNetCore.Diagnostics/StatusCodePage/StatusCodeReExecuteFeature.cs +++ b/src/Microsoft.AspNetCore.Diagnostics/StatusCodePage/StatusCodeReExecuteFeature.cs @@ -8,5 +8,7 @@ namespace Microsoft.AspNetCore.Diagnostics public string OriginalPath { get; set; } public string OriginalPathBase { get; set; } + + public string OriginalQueryString { get; set; } } } \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Diagnostics.Tests/ExceptionHandlerTest.cs b/test/Microsoft.AspNetCore.Diagnostics.Tests/ExceptionHandlerTest.cs index d95a8fe0c9..bd3bad8449 100644 --- a/test/Microsoft.AspNetCore.Diagnostics.Tests/ExceptionHandlerTest.cs +++ b/test/Microsoft.AspNetCore.Diagnostics.Tests/ExceptionHandlerTest.cs @@ -175,6 +175,106 @@ namespace Microsoft.AspNetCore.Diagnostics } } + [Fact] + public async Task Redirect_StatusPage() + { + var expectedStatusCode = 432; + var destination = "/location"; + var builder = new WebHostBuilder() + .Configure(app => + { + app.UseStatusCodePagesWithRedirects("/errorPage?id={0}"); + + app.Map(destination, (innerAppBuilder) => + { + innerAppBuilder.Run((httpContext) => + { + httpContext.Response.StatusCode = expectedStatusCode; + return Task.FromResult(1); + }); + }); + + app.Map("/errorPage", (innerAppBuilder) => + { + innerAppBuilder.Run(async (httpContext) => + { + await httpContext.Response.WriteAsync(httpContext.Request.QueryString.Value); + }); + }); + + app.Run((context) => + { + + throw new InvalidOperationException($"Invalid input provided. {context.Request.Path}"); + }); + }); + var expectedQueryString = $"?id={expectedStatusCode}"; + var expectedUri = $"/errorPage{expectedQueryString}"; + using (var server = new TestServer(builder)) + { + + var client = server.CreateClient(); + var response = await client.GetAsync(destination); + Assert.Equal(HttpStatusCode.Found, response.StatusCode); + Assert.Equal(expectedUri, response.Headers.First(s => s.Key == "Location").Value.First()); + + response = await client.GetAsync(expectedUri); + var content = await response.Content.ReadAsStringAsync(); + Assert.Equal(expectedQueryString, content); + Assert.Equal(expectedQueryString, response.RequestMessage.RequestUri.Query); + } + } + + [Fact] + public async Task Reexecute_RequestWithQueryString() + { + var expectedStatusCode = 432; + var destination = "/location"; + var builder = new WebHostBuilder() + .Configure(app => + { + app.Use(async (context, next) => + { + var beforeNext = context.Request.QueryString; + await next(); + var afterNext = context.Request.QueryString; + + Assert.Equal(beforeNext, afterNext); + }); + app.UseStatusCodePagesWithReExecute("/errorPage", "?id={0}"); + + app.Map(destination, (innerAppBuilder) => + { + innerAppBuilder.Run((httpContext) => + { + httpContext.Response.StatusCode = expectedStatusCode; + return Task.FromResult(1); + }); + }); + + app.Map("/errorPage", (innerAppBuilder) => + { + innerAppBuilder.Run(async (httpContext) => + { + await httpContext.Response.WriteAsync(httpContext.Request.QueryString.Value); + }); + }); + + app.Run((context) => + { + throw new InvalidOperationException("Invalid input provided."); + }); + }); + + using (var server = new TestServer(builder)) + { + var client = server.CreateClient(); + var response = await client.GetAsync(destination); + var content = await response.Content.ReadAsStringAsync(); + Assert.Equal($"?id={expectedStatusCode}", content); + } + } + [Fact] public async Task ClearsCacheHeaders_SetByReexecutionPathHandlers() {