UseHttpsRedirection和AddRedirectToHttpsPermanent在ASP.NET Core中的区别

6
Startup.cs 文件中,请考虑以下内容:
public void ConfigureServices(IServiceCollection services)
{
    // Irrelevant code removed

    services.AddHttpsRedirection(options =>
    {
        options.RedirectStatusCode = StatusCodes.Status301MovedPermanently;
    });
}

// This method gets called by the runtime.
// Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    // Irrelevant code removed

    app.UseHttpsRedirection();

    app.UseRewriter(new RewriteOptions()
        .AddRedirectToWwwPermanent()
        .AddRedirectToHttpsPermanent()
    );
}

据我所知,我必须使用Rewriter来设置AddRedirectToWwwPermanent。我的问题是,我应该同时使用app.UseHttpsRedirection()AddRedirectToHttpsPermanent()吗?或者如果它们做的事情完全相同,那么我应该删除哪一个?
我只是想确保我正确地重定向到Https,并与Www重定向一起使用。
1个回答

15

AddRedirectToHttpsPermanent(或其兄弟 AddRedirectToHttps)将RedirectToHttpsRule添加到重写程序中。此规则的工作原理如下

if (!context.HttpContext.Request.IsHttps)
{
    var host = context.HttpContext.Request.Host;
    if (SSLPort.HasValue && SSLPort.Value > 0)
    {
        // a specific SSL port is specified
        host = new HostString(host.Host, SSLPort.Value);
    }
    else
    {
        // clear the port
        host = new HostString(host.Host);
    }

    var req = context.HttpContext.Request;
    var newUrl = new StringBuilder().Append("https://").Append(host).Append(req.PathBase).Append(req.Path).Append(req.QueryString);
    var response = context.HttpContext.Response;
    response.StatusCode = StatusCode;
    response.Headers[HeaderNames.Location] = newUrl.ToString();
    context.Result = RuleResult.EndResponse;
    context.Logger?.RedirectedToHttps();
}

所以,这基本上获取当前主机名,并构建一个新的URL,看起来与原来相同,只是在前面加上了https://。然后设置301 HTTP状态代码,并通过Location头返回新的URL。
该规则随后作为RewriteMiddleware的一部分执行,其基本上只会循环遍历所有注册的规则,并最终运行上述代码,然后结束响应。
相比之下,这就是HttpsRedirectionMiddleware 内部工作方式
if (context.Request.IsHttps || !TryGetHttpsPort(out var port))
{
    return _next(context);
}

var host = context.Request.Host;
if (port != 443)
{
    host = new HostString(host.Host, port);
}
else
{
    host = new HostString(host.Host);
}

var request = context.Request;
var redirectUrl = UriHelper.BuildAbsolute(
    "https",
    host,
    request.PathBase,
    request.Path,
    request.QueryString);

context.Response.StatusCode = _statusCode;
context.Response.Headers[HeaderNames.Location] = redirectUrl;

_logger.RedirectingToHttps(redirectUrl);

return Task.CompletedTask;

所以这将从传入请求中获取主机名,然后使用UriHelper构建一个绝对URL,该URL看起来与当前请求完全相同,只是使用https://方案。然后它设置了307个HTTP状态码结果,并通过Location头返回新的URL。由于它不调用后期中间件,因此这也将结束响应。
所以是的,这两个解决方案非常不同(不是):它们使用几乎等效的代码并产生相同的结果。唯一的实际区别是,HttpsRedirectionMiddleware默认使用HTTP 307状态码
如果您更喜欢其中一个状态码,则可以完全配置两个中间件以使用您首选的状态码。
所以,你应该使用哪个中间件来启用HTTPS重定向呢?其实并不重要。ASP.NET Core模板默认带有HttpsRedirectionMiddleware,但该中间件仅存在于ASP.NET Core 2.1及以上版本。
我个人会坚持使用HttpsRedirectionMiddleware,因为它非常清晰地传达了其目的。但如果你已经使用了RewriteMiddleware,那么我建议你用RedirectToHttpsRule替换HttpsRedirectionMiddleware,这样你只需要一个单独的中间件来执行重定向。但最终,这并不重要。

感谢@poke提供非常详细的答案!如果我同时使用HttpsRedirectionMiddleware和只设置为执行www重定向的RewriteMiddleware,会不会导致两个301重定向(一个用于https,另一个用于www)?相比在RewriteMiddleware中执行https和www重定向,是否可以将所有内容合并为单个301重定向? - Daniel Congrove
2
不,这两个中间件都会在最后终止请求,而不调用管道中的下一个中间件。因此,先注册的任何中间件都将被执行,然后结束响应。因此不会产生任何效果的组合。 - poke
太好了,这正是我想要的澄清。再次感谢您的帮助! - Daniel Congrove
我不知道你是如何想出这么多细节的,但是 app.UseHttpsRedirection() 似乎使用了一个临时重定向(Status307TemporaryRedirect),而 AddRedirectToHttpsPermanent() 实现了一个永久重定向。(我无法想象为什么有人会想要一个临时重定向到 HTTPS。) - Jonathan Wood

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接