diff --git a/src/Http/Http.Extensions/ref/Microsoft.AspNetCore.Http.Extensions.netcoreapp3.0.cs b/src/Http/Http.Extensions/ref/Microsoft.AspNetCore.Http.Extensions.netcoreapp3.0.cs index fbfcbf7f45..77740aa3f0 100644 --- a/src/Http/Http.Extensions/ref/Microsoft.AspNetCore.Http.Extensions.netcoreapp3.0.cs +++ b/src/Http/Http.Extensions/ref/Microsoft.AspNetCore.Http.Extensions.netcoreapp3.0.cs @@ -12,6 +12,7 @@ namespace Microsoft.AspNetCore.Http public static partial class ResponseExtensions { public static void Clear(this Microsoft.AspNetCore.Http.HttpResponse response) { } + public static void Redirect(this Microsoft.AspNetCore.Http.HttpResponse response, string location, bool permanent, bool preserveMethod) { } } public static partial class SendFileResponseExtensions { diff --git a/src/Http/Http.Extensions/src/ResponseExtensions.cs b/src/Http/Http.Extensions/src/ResponseExtensions.cs index 6c5d92a7af..5fe1614be4 100644 --- a/src/Http/Http.Extensions/src/ResponseExtensions.cs +++ b/src/Http/Http.Extensions/src/ResponseExtensions.cs @@ -3,6 +3,7 @@ using System; using Microsoft.AspNetCore.Http.Features; +using Microsoft.Net.Http.Headers; namespace Microsoft.AspNetCore.Http { @@ -22,5 +23,26 @@ namespace Microsoft.AspNetCore.Http response.Body.SetLength(0); } } + + /// + /// Returns a redirect response (HTTP 301, HTTP 302, HTTP 307 or HTTP 308) to the client. + /// + /// The to redirect. + /// The URL to redirect the client to. This must be properly encoded for use in http headers where only ASCII characters are allowed. + /// True if the redirect is permanent (301 or 308), otherwise false (302 or 307). + /// True if the redirect needs to reuse the method and body (308 or 307), otherwise false (301 or 302). + public static void Redirect(this HttpResponse response, string location, bool permanent, bool preserveMethod) + { + if (preserveMethod) + { + response.StatusCode = permanent ? StatusCodes.Status308PermanentRedirect : StatusCodes.Status307TemporaryRedirect; + } + else + { + response.StatusCode = permanent ? StatusCodes.Status301MovedPermanently : StatusCodes.Status302Found; + } + + response.Headers[HeaderNames.Location] = location; + } } } diff --git a/src/Http/Http.Extensions/test/ResponseExtensionTests.cs b/src/Http/Http.Extensions/test/ResponseExtensionTests.cs index ae6b147fd2..aae5900242 100644 --- a/src/Http/Http.Extensions/test/ResponseExtensionTests.cs +++ b/src/Http/Http.Extensions/test/ResponseExtensionTests.cs @@ -3,8 +3,11 @@ using System; using System.IO; +using System.Linq; +using System.Net; using System.Threading.Tasks; using Microsoft.AspNetCore.Http.Features; +using Microsoft.Net.Http.Headers; using Xunit; namespace Microsoft.AspNetCore.Http.Extensions @@ -35,6 +38,23 @@ namespace Microsoft.AspNetCore.Http.Extensions Assert.Throws(() => context.Response.Clear()); } + [Theory] + [InlineData(true, false, 301)] + [InlineData(false, false, 302)] + [InlineData(true, true, 308)] + [InlineData(false, true, 307)] + public void Redirect_SetsResponseCorrectly(bool permanent, bool preserveMethod, int expectedStatusCode) + { + var location = "http://localhost/redirect"; + var context = new DefaultHttpContext(); + context.Response.StatusCode = StatusCodes.Status200OK; + + context.Response.Redirect(location, permanent, preserveMethod); + + Assert.Equal(location, context.Response.Headers[HeaderNames.Location].First()); + Assert.Equal(expectedStatusCode, context.Response.StatusCode); + } + private class StartedResponseFeature : IHttpResponseFeature { public Stream Body { get; set; }