AspNetCore3.1_Middleware源码解析_3_HttpsRedirection

  • 2020 年 3 月 16 日
  • 笔记

概述

上文提到3.1版本默认没有使用Hsts,但是使用了这个中间件。看名字就很好理解,https跳转,顾名思义,就是跳转到
https地址。

使用场景,当用户使用http访问网站时,自动跳转到https地址。这样更加安全,不需要用户特意输入https://协议。

具体做了些我们一起来看看。

  app.UseHttpsRedirection();

使用方法

跟Hsts一样,HttpsRedirection默认是不需要注入的,除非你需要修改默认配置。

services.AddHttpsRedirection(config =>    {        //https地址的端口号,默认null        config.HttpsPort = 12345;          //跳转响应的状态码,默认307        config.RedirectStatusCode = 302;    });

直接使用中间件即可

 app.UseHttpsRedirection();

源码解析

源代码很简单,只有两个类:HttpsRedirectionOptions配置类,HttpsRedirectionMiddleware中间件

HttpsRedirectionOptions就只有两个配置项

  /// <summary>    /// Options for the HttpsRedirection middleware    /// </summary>    public class HttpsRedirectionOptions    {        /// <summary>        /// The status code used for the redirect response. The default is 307.        /// </summary>        public int RedirectStatusCode { get; set; } = StatusCodes.Status307TemporaryRedirect;          /// <summary>        /// The HTTPS port to be added to the redirected URL.        /// </summary>        /// <remarks>        /// If the HttpsPort is not set, we will try to get the HttpsPort from the following:        /// 1. HTTPS_PORT environment variable        /// 2. IServerAddressesFeature        /// If that fails then the middleware will log a warning and turn off.        /// </remarks>        public int? HttpsPort { get; set; }    }

重点看下中间件做了些什么。代码量很少,大体是这些逻辑。

  • 如果请求是Https,跳过本中间件
  • 中间件会依次尝试从这三个地方取端口号:HttpsRedirectionOptions的配置,HttpsRedirectionOptions,HTTPS_PORT环境变量或配置,IServerAddressesFeature(如果Webhost上绑定了https地址,本中间件能够解析出来端口号)。
  • 如果没有解析出来https的端口号,则跳过本中间件。
  • 如果能够解析出来https端口号,则拼接出来https地址,返回307跳转响应报文(或者配置的其他状态码)。

注:3.1同时支持HTTPS_PORT和ANCM_HTTPS_PORT这两个环境变量。
image

https://docs.microsoft.com/en-us/dotnet/core/compatibility/2.2-3.0

public class HttpsRedirectionMiddleware      {          private const int PortNotFound = -1;            private readonly RequestDelegate _next;          private readonly Lazy<int> _httpsPort;          private readonly int _statusCode;            private readonly IServerAddressesFeature _serverAddressesFeature;          private readonly IConfiguration _config;          private readonly ILogger _logger;            /// <summary>          /// Initializes the HttpsRedirectionMiddleware          /// </summary>          /// <param name="next"></param>          /// <param name="options"></param>          /// <param name="config"></param>          /// <param name="loggerFactory"></param>          public HttpsRedirectionMiddleware(RequestDelegate next, IOptions<HttpsRedirectionOptions> options, IConfiguration config, ILoggerFactory loggerFactory)            {              _next = next ?? throw new ArgumentNullException(nameof(next));              _config = config ?? throw new ArgumentNullException(nameof(config));                if (options == null)              {                  throw new ArgumentNullException(nameof(options));              }              var httpsRedirectionOptions = options.Value;              if (httpsRedirectionOptions.HttpsPort.HasValue)              {                  _httpsPort = new Lazy<int>(() => httpsRedirectionOptions.HttpsPort.Value);              }              else              {                  _httpsPort = new Lazy<int>(TryGetHttpsPort);              }              _statusCode = httpsRedirectionOptions.RedirectStatusCode;              _logger = loggerFactory.CreateLogger<HttpsRedirectionMiddleware>();          }            /// <summary>          /// Initializes the HttpsRedirectionMiddleware          /// </summary>          /// <param name="next"></param>          /// <param name="options"></param>          /// <param name="config"></param>          /// <param name="loggerFactory"></param>          /// <param name="serverAddressesFeature">The</param>          public HttpsRedirectionMiddleware(RequestDelegate next, IOptions<HttpsRedirectionOptions> options, IConfiguration config, ILoggerFactory loggerFactory,              IServerAddressesFeature serverAddressesFeature)              : this(next, options, config, loggerFactory)          {              _serverAddressesFeature = serverAddressesFeature ?? throw new ArgumentNullException(nameof(serverAddressesFeature));          }            /// <summary>          /// Invokes the HttpsRedirectionMiddleware          /// </summary>          /// <param name="context"></param>          /// <returns></returns>          public Task Invoke(HttpContext context)          {              if (context.Request.IsHttps)              {                  return _next(context);              }                var port = _httpsPort.Value;              if (port == PortNotFound)              {                  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;          }            //  Returns PortNotFound (-1) if we were unable to determine the port.          private int TryGetHttpsPort()          {              // The IServerAddressesFeature will not be ready until the middleware is Invoked,              // Order for finding the HTTPS port:              // 1. Set in the HttpsRedirectionOptions              // 2. HTTPS_PORT environment variable              // 3. IServerAddressesFeature              // 4. Fail if not sets                var nullablePort = _config.GetValue<int?>("HTTPS_PORT") ?? _config.GetValue<int?>("ANCM_HTTPS_PORT");              if (nullablePort.HasValue)              {                  var port = nullablePort.Value;                  _logger.PortLoadedFromConfig(port);                  return port;              }                if (_serverAddressesFeature == null)              {                  _logger.FailedToDeterminePort();                  return PortNotFound;              }                foreach (var address in _serverAddressesFeature.Addresses)              {                  var bindingAddress = BindingAddress.Parse(address);                  if (bindingAddress.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase))                  {                      // If we find multiple different https ports specified, throw                      if (nullablePort.HasValue && nullablePort != bindingAddress.Port)                      {                          _logger.FailedMultiplePorts();                          return PortNotFound;                      }                      else                      {                          nullablePort = bindingAddress.Port;                      }                  }              }                if (nullablePort.HasValue)              {                  var port = nullablePort.Value;                  _logger.PortFromServer(port);                  return port;              }                _logger.FailedToDeterminePort();              return PortNotFound;          }      }

OK,完成了。