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; }