ASP.NET Core 中的 Kestrel Web 服务器实现

作者:Tom DykstraChris RossStephen HalterLuke Latham

Kestrel 是一个跨平台的适用于 ASP.NET Core 的 Web 服务器 Kestrel 是 Web 服务器,默认包括在 ASP.NET Core 项目模板中。

Kestrel 支持以下方案:

  • HTTPS
  • 用于启用 WebSocket 的不透明升级
  • 用于获得 Nginx 高性能的 Unix 套接字
  • HTTP/2(除 macOS† 以外)

macOS 的未来版本将支持 †HTTP/2。

.NET Core 支持的所有平台和版本均支持 Kestrel。

查看或下载示例代码如何下载

HTTP/2 支持

如果满足以下基本要求,将为 ASP.NET Core 应用提供 HTTP/2

  • 操作系统†
    • Windows Server 2016/Windows 10 或更高版本‡
    • 具有 OpenSSL 1.0.2 或更高版本的 Linux(例如,Ubuntu 16.04 或更高版本)
  • 目标框架:.NET Core 2.2 或更高版本
  • 应用程序层协议协商 (ALPN) 连接
  • TLS 1.2 或更高版本的连接

macOS 的未来版本将支持 †HTTP/2。 ‡Kestrel 在 Windows Server 2012 R2 和 Windows 8.1 上对 HTTP/2 的支持有限。 支持受限是因为可在这些操作系统上使用的受支持 TLS 密码套件列表有限。 可能需要使用椭圆曲线数字签名算法 (ECDSA) 生成的证书来保护 TLS 连接。

如果已建立 HTTP/2 连接,HttpRequest.Protocol 会报告 HTTP/2

默认情况下,禁用 HTTP/2。 有关配置的详细信息,请参阅 Kestrel 选项ListenOptions.Protocols 部分。

何时结合使用 Kestrel 和反向代理

可以单独使用 Kestrel,也可以将其与反向代理服务器(如 Internet Information Services (IIS)NginxApache)结合使用。 反向代理服务器接收来自网络的 HTTP 请求,并将这些请求转发到 Kestrel。

Kestrel 用作边缘(面向 Internet)Web 服务器:

Kestrel 直接与 Internet 通信,不使用反向代理服务器

Kestrel 用于反向代理配置:

Kestrel 通过反向代理服务器(如 IIS、Nginx 或 Apache)间接与 Internet 进行通信

无论配置是否使用反向代理服务器,都是受支持的托管配置。

在没有反向代理服务器的情况下用作边缘服务器的 Kestrel 不支持在多个进程间共享相同的 IP 和端口。 如果将 Kestrel 配置为侦听某个端口,Kestrel 会处理该端口的所有流量(无视请求的 Host 标头)。 可以共享端口的反向代理能在唯一的 IP 和端口上将请求转发至 Kestrel。

即使不需要反向代理服务器,使用反向代理服务器可能也是个不错的选择。

反向代理:

  • 可以限制所承载的应用中的公开的公共外围应用。
  • 提供额外的配置和防护层。
  • 可以更好地与现有基础结构集成。
  • 简化了负载均和和安全通信 (HTTPS) 配置。 仅反向代理服务器需要 X.509 证书,并且该服务器可使用普通 HTTP 在内部网络上与应用服务器通信。

警告

采用反向代理配置进行托管需要主机筛选

ASP.NET Core 应用中的 Kestrel

默认情况下,ASP.NET Core 项目模板使用 Kestrel。 在“Program.cs”中,ConfigureWebHostDefaults 方法调用 UseKestrel

public static void Main(string[] args)
{
    CreateHostBuilder(args).Build().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });

有关生成主机的详细信息,请参阅 .NET 通用主机 的“设置主机”和“默认生成器设置”部分。

若要在调用 ConfigureWebHostDefaults 后提供其他配置,请使用 ConfigureKestrel

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.ConfigureKestrel(serverOptions =>
            {
                // Set properties and call methods on options
            })
            .UseStartup<Startup>();
        });

Kestrel 选项

Kestrel Web 服务器具有约束配置选项,这些选项在面向 Internet 的部署中尤其有用。

KestrelServerOptions 类的 Limits 属性设置约束。 Limits 属性包含 KestrelServerLimits 类的实例。

下面的示例使用 Microsoft.AspNetCore.Server.Kestrel.Core 命名空间。

using Microsoft.AspNetCore.Server.Kestrel.Core;

在本文后面的示例中,Kestrel 选项是采用 C# 代码配置的。 还可以使用 配置提供程序设置 Kestrel 选项。 例如,文件配置提供程序可以从 appsettings.json 或 appsettings.{Environment}.json 文件加载 Kestrel 配置:

{
  "Kestrel": {
    "Limits": {
      "MaxConcurrentConnections": 100,
      "MaxConcurrentUpgradedConnections": 100
    },
    "DisableStringReuse": true
  }
}

备注

KestrelServerOptions终结点配置 可以通过配置提供程序进行配置。 其余的 Kestrel 配置必须采用 C# 代码进行配置。

使用以下方法之一:

  • Startup.ConfigureServices 中配置 Kestrel:

    1. IConfiguration 的实例注入到 Startup 类中。 下面的示例假定注入的配置已分配给 Configuration 属性。

    2. Startup.ConfigureServices 中,将配置的 Kestrel 部分加载到 Kestrel 的配置中:

      using Microsoft.Extensions.Configuration
      
      public class Startup
      {
          public Startup(IConfiguration configuration)
          {
              Configuration = configuration;
          }
      
          public IConfiguration Configuration { get; }
      
          public void ConfigureServices(IServiceCollection services)
          {
              services.Configure<KestrelServerOptions>(
                  Configuration.GetSection("Kestrel"));
          }
      
          public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
          {
              ...
          }
      }
      
  • 构建主机时配置 Kestrel:

    在 Program.cs 中,将配置的 Kestrel 部分加载到 Kestrel 的配置中:

    // using Microsoft.Extensions.DependencyInjection;
    
    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureServices((context, services) =>
            {
                services.Configure<KestrelServerOptions>(
                    context.Configuration.GetSection("Kestrel"));
            })
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
    

上述两种方法适用于任何配置提供程序

保持活动状态超时

KeepAliveTimeout

获取或设置保持活动状态超时 默认值为 2 分钟。

webBuilder.ConfigureKestrel(serverOptions =>
{
    serverOptions.Limits.MaxConcurrentConnections = 100;
    serverOptions.Limits.MaxConcurrentUpgradedConnections = 100;
    serverOptions.Limits.MaxRequestBodySize = 10 * 1024;
    serverOptions.Limits.MinRequestBodyDataRate =
        new MinDataRate(bytesPerSecond: 100, 
            gracePeriod: TimeSpan.FromSeconds(10));
    serverOptions.Limits.MinResponseDataRate =
        new MinDataRate(bytesPerSecond: 100, 
            gracePeriod: TimeSpan.FromSeconds(10));
    serverOptions.Listen(IPAddress.Loopback, 5000);
    serverOptions.Listen(IPAddress.Loopback, 5001, 
        listenOptions =>
        {
            listenOptions.UseHttps("testCert.pfx", 
                "testPassword");
        });
    serverOptions.Limits.KeepAliveTimeout = 
        TimeSpan.FromMinutes(2);
    serverOptions.Limits.RequestHeadersTimeout = 
        TimeSpan.FromMinutes(1);
})

客户端最大连接数

MaxConcurrentConnections MaxConcurrentUpgradedConnections

可使用以下代码为整个应用设置并发打开的最大 TCP 连接数:

webBuilder.ConfigureKestrel(serverOptions =>
{
    serverOptions.Limits.MaxConcurrentConnections = 100;
    serverOptions.Limits.MaxConcurrentUpgradedConnections = 100;
    serverOptions.Limits.MaxRequestBodySize = 10 * 1024;
    serverOptions.Limits.MinRequestBodyDataRate =
        new MinDataRate(bytesPerSecond: 100, 
            gracePeriod: TimeSpan.FromSeconds(10));
    serverOptions.Limits.MinResponseDataRate =
        new MinDataRate(bytesPerSecond: 100, 
            gracePeriod: TimeSpan.FromSeconds(10));
    serverOptions.Listen(IPAddress.Loopback, 5000);
    serverOptions.Listen(IPAddress.Loopback, 5001, 
        listenOptions =>
        {
            listenOptions.UseHttps("testCert.pfx", 
                "testPassword");
        });
    serverOptions.Limits.KeepAliveTimeout = 
        TimeSpan.FromMinutes(2);
    serverOptions.Limits.RequestHeadersTimeout = 
        TimeSpan.FromMinutes(1);
})

对于已从 HTTP 或 HTTPS 升级到另一个协议(例如,Websocket 请求)的连接,有一个单独的限制。 连接升级后,不会计入 MaxConcurrentConnections 限制。

webBuilder.ConfigureKestrel(serverOptions =>
{
    serverOptions.Limits.MaxConcurrentConnections = 100;
    serverOptions.Limits.MaxConcurrentUpgradedConnections = 100;
    serverOptions.Limits.MaxRequestBodySize = 10 * 1024;
    serverOptions.Limits.MinRequestBodyDataRate =
        new MinDataRate(bytesPerSecond: 100, 
            gracePeriod: TimeSpan.FromSeconds(10));
    serverOptions.Limits.MinResponseDataRate =
        new MinDataRate(bytesPerSecond: 100, 
            gracePeriod: TimeSpan.FromSeconds(10));
    serverOptions.Listen(IPAddress.Loopback, 5000);
    serverOptions.Listen(IPAddress.Loopback, 5001, 
        listenOptions =>
        {
            listenOptions.UseHttps("testCert.pfx", 
                "testPassword");
        });
    serverOptions.Limits.KeepAliveTimeout = 
        TimeSpan.FromMinutes(2);
    serverOptions.Limits.RequestHeadersTimeout = 
        TimeSpan.FromMinutes(1);
})

默认情况下,最大连接数不受限制 (NULL)。

请求正文最大大小

MaxRequestBodySize

默认的请求正文最大大小为 30,000,000 字节,大约 28.6 MB。

在 ASP.NET Core MVC 应用中替代限制的推荐方法是在操作方法上使用 RequestSizeLimitAttribute 属性:

[RequestSizeLimit(100000000)]
public IActionResult MyActionMethod()

以下示例演示如何为每个请求上的应用配置约束:

webBuilder.ConfigureKestrel(serverOptions =>
{
    serverOptions.Limits.MaxConcurrentConnections = 100;
    serverOptions.Limits.MaxConcurrentUpgradedConnections = 100;
    serverOptions.Limits.MaxRequestBodySize = 10 * 1024;
    serverOptions.Limits.MinRequestBodyDataRate =
        new MinDataRate(bytesPerSecond: 100, 
            gracePeriod: TimeSpan.FromSeconds(10));
    serverOptions.Limits.MinResponseDataRate =
        new MinDataRate(bytesPerSecond: 100, 
            gracePeriod: TimeSpan.FromSeconds(10));
    serverOptions.Listen(IPAddress.Loopback, 5000);
    serverOptions.Listen(IPAddress.Loopback, 5001, 
        listenOptions =>
        {
            listenOptions.UseHttps("testCert.pfx", 
                "testPassword");
        });
    serverOptions.Limits.KeepAliveTimeout = 
        TimeSpan.FromMinutes(2);
    serverOptions.Limits.RequestHeadersTimeout = 
        TimeSpan.FromMinutes(1);
})

在中间件中替代特定请求的设置:

app.Run(async (context) =>
{
    context.Features.Get<IHttpMaxRequestBodySizeFeature>()
        .MaxRequestBodySize = 10 * 1024;

    var minRequestRateFeature = 
        context.Features.Get<IHttpMinRequestBodyDataRateFeature>();
    var minResponseRateFeature = 
        context.Features.Get<IHttpMinResponseDataRateFeature>();

    if (minRequestRateFeature != null)
    {
        minRequestRateFeature.MinDataRate = new MinDataRate(
            bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(10));
    }

    if (minResponseRateFeature != null)
    {
        minResponseRateFeature.MinDataRate = new MinDataRate(
            bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(10));
    }

如果应用在开始读取请求后配置请求限制,则会引发异常。 IsReadOnly 属性指示 MaxRequestBodySize 属性处于只读状态,意味已经无法再配置限制。

当应用在 ASP.NET Core 模块后于进程外运行时,由于 IIS 已设置限制,因此禁用了 Kestrel 的请求正文大小限制。

请求正文最小数据速率

MinRequestBodyDataRate MinResponseDataRate

Kestrel 每秒检查一次数据是否以指定的速率(字节/秒)传入。 如果速率低于最小值,则连接超时。宽限期是 Kestrel 提供给客户端用于将其发送速率提升到最小值的时间量;在此期间不会检查速率。 宽限期有助于避免最初由于 TCP 慢启动而以较慢速率发送数据的连接中断。

默认的最小速率为 240 字节/秒,包含 5 秒的宽限期。

最小速率也适用于响应。 除了属性和接口名称中具有 RequestBodyResponse 以外,用于设置请求限制和响应限制的代码相同。

以下示例演示如何在 Program.cs 中配置最小数据速率:

webBuilder.ConfigureKestrel(serverOptions =>
{
    serverOptions.Limits.MaxConcurrentConnections = 100;
    serverOptions.Limits.MaxConcurrentUpgradedConnections = 100;
    serverOptions.Limits.MaxRequestBodySize = 10 * 1024;
    serverOptions.Limits.MinRequestBodyDataRate =
        new MinDataRate(bytesPerSecond: 100, 
            gracePeriod: TimeSpan.FromSeconds(10));
    serverOptions.Limits.MinResponseDataRate =
        new MinDataRate(bytesPerSecond: 100, 
            gracePeriod: TimeSpan.FromSeconds(10));
    serverOptions.Listen(IPAddress.Loopback, 5000);
    serverOptions.Listen(IPAddress.Loopback, 5001, 
        listenOptions =>
        {
            listenOptions.UseHttps("testCert.pfx", 
                "testPassword");
        });
    serverOptions.Limits.KeepAliveTimeout = 
        TimeSpan.FromMinutes(2);
    serverOptions.Limits.RequestHeadersTimeout = 
        TimeSpan.FromMinutes(1);
})

在中间件中替代每个请求的最低速率限制:

app.Run(async (context) =>
{
    context.Features.Get<IHttpMaxRequestBodySizeFeature>()
        .MaxRequestBodySize = 10 * 1024;

    var minRequestRateFeature = 
        context.Features.Get<IHttpMinRequestBodyDataRateFeature>();
    var minResponseRateFeature = 
        context.Features.Get<IHttpMinResponseDataRateFeature>();

    if (minRequestRateFeature != null)
    {
        minRequestRateFeature.MinDataRate = new MinDataRate(
            bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(10));
    }

    if (minResponseRateFeature != null)
    {
        minResponseRateFeature.MinDataRate = new MinDataRate(
            bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(10));
    }

用于 HTTP/2 请求的 HttpContext.Features 中不存在前面示例中引用的 IHttpMinResponseDataRateFeature,因为鉴于协议支持请求多路复用,HTTP/2 通常不支持按请求修改速率限制。 不过,用于 HTTP/2 请求的 HttpContext.Features 中仍存在 IHttpMinRequestBodyDataRateFeature,因为仍可以通过将 IHttpMinRequestBodyDataRateFeature.MinDataRate 设置为 null(甚至对于 HTTP/2 请求),按请求完全禁用读取速率限制。 对于给定 HTTP/2 请求,尝试读取 IHttpMinRequestBodyDataRateFeature.MinDataRate 或尝试将它设置为除 null 以外的值会导致 NotSupportedException 抛出。

通过 KestrelServerOptions.Limits 配置的服务器范围的速率限制仍适用于 HTTP/1.x 和 HTTP/2 连接。

请求标头超时

RequestHeadersTimeout

获取或设置服务器接收请求标头所花费的最大时间量。 默认值为 30 秒。

webBuilder.ConfigureKestrel(serverOptions =>
{
    serverOptions.Limits.MaxConcurrentConnections = 100;
    serverOptions.Limits.MaxConcurrentUpgradedConnections = 100;
    serverOptions.Limits.MaxRequestBodySize = 10 * 1024;
    serverOptions.Limits.MinRequestBodyDataRate =
        new MinDataRate(bytesPerSecond: 100, 
            gracePeriod: TimeSpan.FromSeconds(10));
    serverOptions.Limits.MinResponseDataRate =
        new MinDataRate(bytesPerSecond: 100, 
            gracePeriod: TimeSpan.FromSeconds(10));
    serverOptions.Listen(IPAddress.Loopback, 5000);
    serverOptions.Listen(IPAddress.Loopback, 5001, 
        listenOptions =>
        {
            listenOptions.UseHttps("testCert.pfx", 
                "testPassword");
        });
    serverOptions.Limits.KeepAliveTimeout = 
        TimeSpan.FromMinutes(2);
    serverOptions.Limits.RequestHeadersTimeout = 
        TimeSpan.FromMinutes(1);
})

每个连接的最大流

Http2.MaxStreamsPerConnection 限制每个 HTTP/2 连接的并发请求流的数量。 拒绝过多的流。

webBuilder.ConfigureKestrel(serverOptions =>
{
    serverOptions.Limits.Http2.MaxStreamsPerConnection = 100;
});

默认值为 100。

标题表大小

HPACK 解码器解压缩 HTTP/2 连接的 HTTP 标头。 Http2.HeaderTableSize 限制 HPACK 解码器使用的标头压缩表的大小。 该值以八位字节提供,且必须大于零 (0)。

webBuilder.ConfigureKestrel(serverOptions =>
{
    serverOptions.Limits.Http2.HeaderTableSize = 4096;
});

默认值为 4096。

最大帧大小

Http2.MaxFrameSize 表示服务器接收或发送的 HTTP/2 连接帧有效负载的最大允许大小。 该值以八位字节提供,必须介于 2^14 (16,384) 和 2^24-1 (16,777,215) 之间。

webBuilder.ConfigureKestrel(serverOptions =>
{
    serverOptions.Limits.Http2.MaxFrameSize = 16384;
});

默认值为 2^14 (16,384)。

最大请求标头大小

Http2.MaxRequestHeaderFieldSize 表示请求标头值的允许的最大大小(用八进制表示)。 此限制适用于名称和值的压缩和未压缩表示形式。 该值必须大于零 (0)。

webBuilder.ConfigureKestrel(serverOptions =>
{
    serverOptions.Limits.Http2.MaxRequestHeaderFieldSize = 8192;
});

默认值为 8,192。

初始连接窗口大小

Http2.InitialConnectionWindowSize 表示服务器一次性缓存的最大请求主体数据大小(每次连接时在所有请求(流)中汇总,以字节为单位)。 请求也受 Http2.InitialStreamWindowSize 限制。 该值必须大于或等于 65,535,并小于 2^31 (2,147,483,648)。

webBuilder.ConfigureKestrel(serverOptions =>
{
    serverOptions.Limits.Http2.InitialConnectionWindowSize = 131072;
});

默认值为 128 KB (131,072)。

初始流窗口大小

Http2.InitialStreamWindowSize 表示服务器针对每个请求(流)的一次性缓存的最大请求主体数据大小(以字节为单位)。 请求也受 Http2.InitialConnectionWindowSize 限制。 该值必须大于或等于 65,535,并小于 2^31 (2,147,483,648)。

webBuilder.ConfigureKestrel(serverOptions =>
{
    serverOptions.Limits.Http2.InitialStreamWindowSize = 98304;
});

默认值为 96 KB (98,304)。

同步 IO

AllowSynchronousIO 控制是否允许对请求和响应使用同步 IO。 默认值为 false

警告

大量的阻止同步 IO 操作可能会导致线程池资源不足,进而导致应用无响应。 仅在使用不支持异步 IO 的库时,才启用 AllowSynchronousIO

下面的示例启用同步 IO:

webBuilder.ConfigureKestrel(serverOptions =>
{
    serverOptions.AllowSynchronousIO = true;
})

有关其他 Kestrel 选项和限制的信息,请参阅:

终结点配置

默认情况下,ASP.NET Core 绑定到:

  • http://localhost:5000
  • https://localhost:5001(存在本地开发证书时)

使用以下内容指定 URL:

  • ASPNETCORE_URLS 环境变量。
  • --urls 命令行参数。
  • urls 主机配置键。
  • UseUrls 扩展方法。

采用这些方法提供的值可以是一个或多个 HTTP 和 HTTPS 终结点(如果默认证书可用,则为 HTTPS)。 将值配置为以分号分隔的列表(例如 "Urls": "http://localhost:8000;http://localhost:8001")。

有关这些方法的详细信息,请参阅服务器 URL重写配置

关于开发证书的创建:

某些浏览器需要授予显式权限才能信任本地开发证书。

项目模板将应用配置为默认情况下在 HTTPS 上运行,并包括 HTTPS 重定向和 HSTS 支持

调用 KestrelServerOptions 上的 ListenListenUnixSocket 方法以配置 URL 前缀和 Kestrel 的端口。

UseUrls--urls 命令行参数、urls 主机配置键以及 ASPNETCORE_URLS 环境变量也有用,但具有本节后面注明的限制(必须要有可用于 HTTPS 终结点配置的默认证书)。

KestrelServerOptions 配置:

ConfigureEndpointDefaults(Action<ListenOptions>)

指定一个为每个指定的终结点运行的配置 Action 多次调用 ConfigureEndpointDefaults,用最新指定的 Action 替换之前的 Action

webBuilder.ConfigureKestrel(serverOptions =>
{
    serverOptions.ConfigureEndpointDefaults(listenOptions =>
    {
        // Configure endpoint defaults
    });
});

备注

通过在调用 ConfigureEndpointDefaults 之前调用 Listen 创建的终结点将不会应用默认值。

ConfigureHttpsDefaults(Action<HttpsConnectionAdapterOptions>)

指定一个为每个 HTTPS 终结点运行的配置 Action 多次调用 ConfigureHttpsDefaults,用最新指定的 Action 替换之前的 Action

webBuilder.ConfigureKestrel(serverOptions =>
{
    serverOptions.ConfigureHttpsDefaults(listenOptions =>
    {
        // certificate is an X509Certificate2
        listenOptions.ServerCertificate = certificate;
    });
});

备注

通过在调用 ConfigureHttpsDefaults 之前调用 Listen 创建的终结点将不会应用默认值。

Configure(IConfiguration)

创建配置加载程序,用于设置将 IConfiguration 作为输入的 Kestrel。 配置必须针对 Kestrel 的配置节。

ListenOptions.UseHttps

将 Kestrel 配置为使用 HTTPS。

ListenOptions.UseHttps 扩展:

  • UseHttps – 将 Kestrel 配置为使用 HTTPS,采用默认证书。 如果没有配置默认证书,则会引发异常。
  • UseHttps(string fileName)
  • UseHttps(string fileName, string password)
  • UseHttps(string fileName, string password, Action<HttpsConnectionAdapterOptions> configureOptions)
  • UseHttps(StoreName storeName, string subject)
  • UseHttps(StoreName storeName, string subject, bool allowInvalid)
  • UseHttps(StoreName storeName, string subject, bool allowInvalid, StoreLocation location)
  • UseHttps(StoreName storeName, string subject, bool allowInvalid, StoreLocation location, Action<HttpsConnectionAdapterOptions> configureOptions)
  • UseHttps(X509Certificate2 serverCertificate)
  • UseHttps(X509Certificate2 serverCertificate, Action<HttpsConnectionAdapterOptions> configureOptions)
  • UseHttps(Action<HttpsConnectionAdapterOptions> configureOptions)

ListenOptions.UseHttps 参数:

  • filename 是证书文件的路径和文件名,关联包含应用内容文件的目录。
  • password 是访问 X.509 证书数据所需的密码。
  • configureOptions 是配置 HttpsConnectionAdapterOptionsAction 返回 ListenOptions
  • storeName 是从中加载证书的证书存储。
  • subject 是证书的主题名称。
  • allowInvalid 指示是否存在需要留意的无效证书,例如自签名证书。
  • location 是从中加载证书的存储位置。
  • serverCertificate 是 X.509 证书。

在生产中,必须显式配置 HTTPS。 至少必须提供默认证书。

下面要描述的支持的配置:

  • 无配置
  • 从配置中替换默认证书
  • 更改代码中的默认值

无配置

Kestrel 在 http://localhost:5000https://localhost:5001 上进行侦听(如果默认证书可用)。

从配置中替换默认证书

CreateDefaultBuilder 在默认情况下调用 Configure(context.Configuration.GetSection("Kestrel")) 来加载 Kestrel 配置。 Kestrel 可以使用默认 HTTPS 应用设置配置架构。 从磁盘上的文件或从证书存储中配置多个终结点,包括要使用的 URL 和证书。

在以下 appsettings.json 示例中:

  • 将 AllowInvalid 设置为 true,从而允许使用无效证书(例如自签名证书)。
  • 任何未指定证书的 HTTPS 终结点(下例中的 HttpsDefaultCert)会回退至在 Certificates > Default 下定义的证书或开发证书。
{
  "Kestrel": {
    "Endpoints": {
      "Http": {
        "Url": "http://localhost:5000"
      },
      "HttpsInlineCertFile": {
        "Url": "https://localhost:5001",
        "Certificate": {
          "Path": "<path to .pfx file>",
          "Password": "<certificate password>"
        }
      },
      "HttpsInlineCertStore": {
        "Url": "https://localhost:5002",
        "Certificate": {
          "Subject": "<subject; required>",
          "Store": "<certificate store; required>",
          "Location": "<location; defaults to CurrentUser>",
          "AllowInvalid": "<true or false; defaults to false>"
        }
      },
      "HttpsDefaultCert": {
        "Url": "https://localhost:5003"
      },
      "Https": {
        "Url": "https://*:5004",
        "Certificate": {
          "Path": "<path to .pfx file>",
          "Password": "<certificate password>"
        }
      }
    },
    "Certificates": {
      "Default": {
        "Path": "<path to .pfx file>",
        "Password": "<certificate password>"
      }
    }
  }
}

此外还可以使用任何证书节点的 Path 和 Password,采用证书存储字段指定证书。 例如,可将 Certificates > Default 证书指定为:

"Default": {
  "Subject": "<subject; required>",
  "Store": "<cert store; required>",
  "Location": "<location; defaults to CurrentUser>",
  "AllowInvalid": "<true or false; defaults to false>"
}

架构的注意事项:

  • 终结点的名称不区分大小写。 例如,HTTPSHttps 都是有效的。
  • 每个终结点都要具备 Url 参数。 此参数的格式和顶层 Urls 配置参数一样,只不过它只能有单个值。
  • 这些终结点不会添加进顶层 Urls 配置中定义的终结点,而是替换它们。 通过 Listen 在代码中定义的终结点与在配置节中定义的终结点相累积。
  • Certificate 部分是可选的。 如果为指定 Certificate 部分,则使用在之前的方案中定义的默认值。 如果没有可用的默认值,服务器会引发异常且无法启动。
  • Certificate 支持 Path–Password 和 Subject–Store 证书。
  • 只要不会导致端口冲突,就能以这种方式定义任何数量的终结点。
  • options.Configure(context.Configuration.GetSection("{SECTION}")) 通过 .Endpoint(string name, listenOptions => { }) 方法返回 KestrelConfigurationLoader,可以用于补充已配置的终结点设置:
webBuilder.UseKestrel((context, serverOptions) =>
{
    serverOptions.Configure(context.Configuration.GetSection("Kestrel"))
        .Endpoint("HTTPS", listenOptions =>
        {
            listenOptions.HttpsOptions.SslProtocols = SslProtocols.Tls12;
        });
});

可以直接访问 KestrelServerOptions.ConfigurationLoader 以继续迭代现有加载程序,例如由 CreateDefaultBuilder 提供的加载程序。

  • 每个终结点的配置节都可用于 Endpoint 方法中的选项,以便读取自定义设置。
  • 通过另一节再次调用 options.Configure(context.Configuration.GetSection("{SECTION}")) 可能加载多个配置。 只使用最新配置,除非之前的实例上显式调用了 Load 元包不会调用 Load,所以可能会替换它的默认配置节。
  • KestrelConfigurationLoaderKestrelServerOptions 将 API 的 Listen 簇反射为 Endpoint 重载,因此可在同样的位置配置代码和配置终结点。 这些重载不使用名称,且只使用配置中的默认设置。

更改代码中的默认值

可以使用 ConfigureEndpointDefaultsConfigureHttpsDefaults 更改 ListenOptionsHttpsConnectionAdapterOptions 的默认设置,包括重写之前的方案指定的默认证书。 需要在配置任何终结点之前调用 ConfigureEndpointDefaultsConfigureHttpsDefaults

webBuilder.ConfigureKestrel(serverOptions =>
{
    serverOptions.ConfigureEndpointDefaults(listenOptions =>
    {
        // Configure endpoint defaults
    });

    serverOptions.ConfigureHttpsDefaults(listenOptions =>
    {
        listenOptions.SslProtocols = SslProtocols.Tls12;
    });
});

SNI 的 Kestrel 支持

服务器名称指示 (SNI) 可用于承载相同 IP 地址和端口上的多个域。 为了运行 SNI,客户端在 TLS 握手过程中将进行安全会话的主机名发送至服务器,从而让服务器可以提供正确的证书。 在 TLS 握手后的安全会话期间,客户端将服务器提供的证书用于与服务器进行加密通信。

Kestrel 通过 ServerCertificateSelector 回调支持 SNI。 每次连接调用一次回调,从而允许应用检查主机名并选择合适的证书。

SNI 支持要求:

  • 在目标框架 netcoreapp2.1 或更高版本上运行。 net461 或最高版本上,将调用回调,但是 name 始终为 null 如果客户端未在 TLS 握手过程中提供主机名参数,则 name 也为 null
  • 所有网站在相同的 Kestrel 实例上运行。 Kestrel 在无反向代理时不支持跨多个实例共享一个 IP 地址和端口。
webBuilder.ConfigureKestrel(serverOptions =>
{
    serverOptions.ListenAnyIP(5005, listenOptions =>
    {
        listenOptions.UseHttps(httpsOptions =>
        {
            var localhostCert = CertificateLoader.LoadFromStoreCert(
                "localhost", "My", StoreLocation.CurrentUser,
                allowInvalid: true);
            var exampleCert = CertificateLoader.LoadFromStoreCert(
                "example.com", "My", StoreLocation.CurrentUser,
                allowInvalid: true);
            var subExampleCert = CertificateLoader.LoadFromStoreCert(
                "sub.example.com", "My", StoreLocation.CurrentUser,
                allowInvalid: true);
            var certs = new Dictionary<string, X509Certificate2>(
                StringComparer.OrdinalIgnoreCase);
            certs["localhost"] = localhostCert;
            certs["example.com"] = exampleCert;
            certs["sub.example.com"] = subExampleCert;

            httpsOptions.ServerCertificateSelector = (connectionContext, name) =>
            {
                if (name != null && certs.TryGetValue(name, out var cert))
                {
                    return cert;
                }

                return exampleCert;
            };
        });
    });
});

连接日志记录

调用 UseConnectionLogging 以发出用于进行连接上的字节级别通信的调试级别日志。 连接日志记录有助于排查低级通信中的问题,例如在 TLS 加密期间和代理后。 如果 UseConnectionLogging 放置在 UseHttps 之前,则会记录加密的流量。 如果 UseConnectionLogging 放置于 UseHttps 之后,则会记录解密的流量。

webBuilder.ConfigureKestrel(serverOptions =>
{
    serverOptions.Listen(IPAddress.Any, 8000, listenOptions =>
    {
        listenOptions.UseConnectionLogging();
    });
});

绑定到 TCP 套接字

Listen 方法绑定至 TCP 套接字,且 options lambda 允许 X.509 证书配置:

public static void Main(string[] args)
{
    CreateHostBuilder(args).Build().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.ConfigureKestrel(serverOptions =>
            {
                serverOptions.Listen(IPAddress.Loopback, 5000);
                serverOptions.Listen(IPAddress.Loopback, 5001, 
                    listenOptions =>
                    {
                        listenOptions.UseHttps("testCert.pfx", 
                            "testPassword");
                    });
            })
            .UseStartup<Startup>();
        });

示例使用 ListenOptions 为终结点配置 HTTPS。 可使用相同 API 为特定终结点配置其他 Kestrel 设置。

在 Windows 上,可以使用 New-SelfSignedCertificate PowerShell cmdlet 创建自签名证书。 有关不支持的示例,请参阅 UpdateIISExpressSSLForChrome.ps1

在 macOS、Linux 和 Windows 上,可以使用 OpenSSL 创建证书。

绑定到 Unix 套接字

可通过 ListenUnixSocket 侦听 Unix 套接字以提高 Nginx 的性能,如以下示例所示:

webBuilder.ConfigureKestrel(serverOptions =>
{
    serverOptions.ListenUnixSocket("/tmp/kestrel-test.sock");
    serverOptions.ListenUnixSocket("/tmp/kestrel-test.sock", 
        listenOptions =>
        {
            listenOptions.UseHttps("testCert.pfx", 
                "testpassword");
        });
})
  • 在 Nginx confiuguration 文件中,将 server > location > proxy_pass 条目设置为 http://unix:/tmp/{KESTREL SOCKET}:/; {KESTREL SOCKET} 是提供给 ListenUnixSocket 的套接字的名称(例如,上述示例中的 kestrel-test.sock)。
  • 确保套接字可由 Nginx (例如 chmod go+w /tmp/kestrel-test.sock)进行写入。

端口 0

如果指定端口号 0,Kestrel 将动态绑定到可用端口。 以下示例演示如何确定 Kestrel 在运行时实际绑定到的端口:

public void Configure(IApplicationBuilder app)
{
    var serverAddressesFeature = 
        app.ServerFeatures.Get<IServerAddressesFeature>();

    app.UseStaticFiles();

    app.Run(async (context) =>
    {
        context.Response.ContentType = "text/html";
        await context.Response
            .WriteAsync("<!DOCTYPE html><html lang=\"en\"><head>" +
                "<title></title></head><body><p>Hosted by Kestrel</p>");

        if (serverAddressesFeature != null)
        {
            await context.Response
                .WriteAsync("<p>Listening on the following addresses: " +
                    string.Join(", ", serverAddressesFeature.Addresses) +
                    "</p>");
        }

        await context.Response.WriteAsync("<p>Request URL: " +
            $"{context.Request.GetDisplayUrl()}<p>");
    });
}

在应用运行时,控制台窗口输出指示可用于访问应用的动态端口:

Listening on the following addresses: http://127.0.0.1:48508

限制

使用以下方法配置终结点:

  • UseUrls
  • --urls 命令行参数
  • urls 主机配置键
  • ASPNETCORE_URLS 环境变量

若要将代码用于 Kestrel 以外的服务器,这些方法非常有用。 不过,请注意以下限制:

  • HTTPS 无法与这些方法结合使用,除非在 HTTPS 终结点配置中提供了默认证书(例如,使用 KestrelServerOptions 配置或配置文件,如本主题前面的部分所示)。
  • 如果同时使用 ListenUseUrls 方法,Listen 终结点将覆盖 UseUrls 终结点。

IIS 终结点配置

使用 IIS 时,由 ListenUseUrls 设置用于 IIS 覆盖绑定的 URL 绑定。 有关详细信息,请参阅 ASP.NET Core 模块主题。

ListenOptions.Protocols

Protocols 属性建立在连接终结点上或为服务器启用的 HTTP 协议(HttpProtocols)。 HttpProtocols 枚举向 Protocols 属性赋值。

HttpProtocols 枚举值 允许的连接协议
Http1 仅 HTTP/1.1。 可以在具有 TLS 或没有 TLS 的情况下使用。
Http2 仅 HTTP/2。 仅当客户端支持先验知识模式时,才可以在没有 TLS 的情况下使用。
Http1AndHttp2 HTTP/1.1 和 HTTP/2。 HTTP/2 要求客户端在 TLS 应用层协议协商 (ALPN) 握手过程中选择 HTTP/2;否则,连接默认为 HTTP/1.1。

任何终结点的默认 ListenOptions.Protocols 值都为 HttpProtocols.Http1AndHttp2

HTTP/2 的 TLS 限制:

  • TLS 版本 1.2 或更高版本
  • 重新协商已禁用
  • 压缩已禁用
  • 最小的临时密钥交换大小:
    • 椭圆曲线 Diffie-Hellman (ECDHE) [RFC4492] – 最小 224 位
    • 有限字段 Diffie-Hellman (DHE) [TLS12] – 最小 2048 位
  • 密码套件未列入阻止列表

默认情况下,支持具有 P-256 椭圆曲线 [FIPS186] 的 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 [TLS-ECDHE]。

以下示例允许端口 8000 上的 HTTP/1.1 和 HTTP/2 连接。 TLS 使用提供的证书来保护连接:

webBuilder.ConfigureKestrel(serverOptions =>
{
    serverOptions.Listen(IPAddress.Any, 8000, listenOptions =>
    {
        listenOptions.UseHttps("testCert.pfx", "testPassword");
    });
});

使用连接中间件,针对特定密码的每个连接筛选 TLS 握手(如有必要)。

下面的示例针对应用不支持的任何密码算法引发 NotSupportedException 或者,定义 ITlsHandshakeFeature.CipherAlgorithm 并将其与可接受的密码套件列表进行比较。

没有哪种加密是使用 CipherAlgorithmType.Null 密码算法。

// using System.Net;
// using Microsoft.AspNetCore.Connections;

webBuilder.ConfigureKestrel(serverOptions =>
{
    serverOptions.Listen(IPAddress.Any, 8000, listenOptions =>
    {
        listenOptions.UseHttps("testCert.pfx", "testPassword");
        listenOptions.UseTlsFilter();
    });
});
using System;
using System.Security.Authentication;
using Microsoft.AspNetCore.Connections.Features;

namespace Microsoft.AspNetCore.Connections
{
    public static class TlsFilterConnectionMiddlewareExtensions
    {
        public static IConnectionBuilder UseTlsFilter(
            this IConnectionBuilder builder)
        {
            return builder.Use((connection, next) =>
            {
                var tlsFeature = connection.Features.Get<ITlsHandshakeFeature>();

                if (tlsFeature.CipherAlgorithm == CipherAlgorithmType.Null)
                {
                    throw new NotSupportedException("Prohibited cipher: " +
                        tlsFeature.CipherAlgorithm);
                }

                return next();
            });
        }
    }
}

连接筛选也可以通过 IConnectionBuilder lambda 进行配置:

// using System;
// using System.Net;
// using System.Security.Authentication;
// using Microsoft.AspNetCore.Connections;
// using Microsoft.AspNetCore.Connections.Features;

webBuilder.ConfigureKestrel(serverOptions =>
{
    serverOptions.Listen(IPAddress.Any, 8000, listenOptions =>
    {
        listenOptions.UseHttps("testCert.pfx", "testPassword");
        listenOptions.Use((context, next) =>
        {
            var tlsFeature = context.Features.Get<ITlsHandshakeFeature>();

            if (tlsFeature.CipherAlgorithm == CipherAlgorithmType.Null)
            {
                throw new NotSupportedException(
                    $"Prohibited cipher: {tlsFeature.CipherAlgorithm}");
            }

            return next();
        });
    });
});

在 Linux 上,CipherSuitesPolicy 可用于针对每个连接筛选 TLS 握手:

// using System.Net.Security;
// using Microsoft.AspNetCore.Hosting;
// using Microsoft.AspNetCore.Server.Kestrel.Core;
// using Microsoft.Extensions.DependencyInjection;
// using Microsoft.Extensions.Hosting;

webBuilder.ConfigureKestrel(serverOptions =>
{
    serverOptions.ConfigureHttpsDefaults(listenOptions =>
    {
        listenOptions.OnAuthenticate = (context, sslOptions) =>
        {
            sslOptions.CipherSuitesPolicy = new CipherSuitesPolicy(
                new[]
                {
                    TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
                    TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
                    // ...
                });
        };
    });
});

从配置中设置协议

CreateDefaultBuilder 在默认情况下调用 serverOptions.Configure(context.Configuration.GetSection("Kestrel")) 来加载 Kestrel 配置。

以下 appsettings.json 示例将 HTTP/1.1 建立为所有终结点的默认连接协议:

{
  "Kestrel": {
    "EndpointDefaults": {
      "Protocols": "Http1"
    }
  }
}

以下 appsettings.json 示例将 HTTP/1.1 建立为所有指定终结点的连接协议:

{
  "Kestrel": {
    "Endpoints": {
      "HttpsDefaultCert": {
        "Url": "https://localhost:5001",
        "Protocols": "Http1"
      }
    }
  }
}

代码中指定的协议覆盖了由配置设置的值。

传输配置

对于需要使用 Libuv (UseLibuv) 的项目:

  • 将用于 Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv 包的依赖项添加到应用的项目文件:

    <PackageReference Include="Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv"
                       Version="{VERSION}" />
    
  • 调用 IWebHostBuilder 上的 UseLibuv

    public class Program
    {
         public static void Main(string[] args)
         {
             CreateHostBuilder(args).Build().Run();
         }
    
         public static IHostBuilder CreateHostBuilder(string[] args) =>
             Host.CreateDefaultBuilder(args)
                 .ConfigureWebHostDefaults(webBuilder =>
                 {
                     webBuilder.UseLibuv();
                     webBuilder.UseStartup<Startup>();
                 });
    }
    

URL 前缀

如果使用 UseUrls--urls 命令行参数、urls 主机配置键或 ASPNETCORE_URLS 环境变量,URL 前缀可采用以下任意格式。

仅 HTTP URL 前缀是有效的。 使用 UseUrls 配置 URL 绑定时,Kestrel 不支持 HTTPS。

  • 包含端口号的 IPv4 地址

    http://65.55.39.10:80/
    

    0.0.0.0 是一种绑定到所有 IPv4 地址的特殊情况。

  • 包含端口号的 IPv6 地址

    http://[0:0:0:0:0:ffff:4137:270a]:80/
    

    [::] 是 IPv4 0.0.0.0 的 IPv6 等效项。

  • 包含端口号的主机名

    http://contoso.com:80/
    http://*:80/
    

    主机名、*+ 并不特殊。 没有识别为有效 IP 地址或 localhost 的任何内容都将绑定到所有 IPv4 和 IPv6 IP。 若要将不同主机名绑定到相同端口上的不同 ASP.NET Core 应用,请使用 HTTP.sys 或 IIS、Nginx 或 Apache 等反向代理服务器。

    警告

    采用反向代理配置进行托管需要主机筛选

  • 包含端口号的主机 localhost 名称或包含端口号的环回 IP

    http://localhost:5000/
    http://127.0.0.1:5000/
    http://[::1]:5000/
    

    指定 localhost 后,Kestrel 将尝试绑定到 IPv4 和 IPv6 环回接口。 如果其他服务正在任一环回接口上使用请求的端口,则 Kestrel 将无法启动。 如果任一环回接口出于任何其他原因(通常是因为 IPv6 不受支持)而不可用,则 Kestrel 将记录一个警告。

主机筛选

尽管 Kestrel 支持基于前缀的配置(例如 http://example.com:5000),但 Kestrel 在很大程度上会忽略主机名。 主机 localhost 是一个特殊情况,用于绑定至环回地址。 除了显式 IP 地址以外的所有主机都绑定至所有公共 IP 地址。 不验证 Host 标头。

解决方法是,使用主机筛选中间件。 主机筛选中间件由 Microsoft.AspNetCore.HostFiltering 包提供,此包为 ASP.NET Core 应用隐式提供。 由调用 AddHostFilteringCreateDefaultBuilder 添加中间件:

public class Program
{
    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>();
}

默认情况下,主机筛选中间件处于禁用状态。 要启用该中间件,请在 appsettings.json/appsettings.<EnvironmentName>.json 中定义一个 AllowedHosts 键。 此值是以分号分隔的不带端口号的主机名列表:

appsettings.json:

{
  "AllowedHosts": "example.com;localhost"
}

备注

转接头中间件 同样包含 AllowedHosts 选项。 转接头中间件和主机筛选中间件具有适合不同方案的相似功能。 如果未保留 Host 标头,并且使用反向代理服务器或负载均衡器转接请求,则使用转接头中间件设置 AllowedHosts 比较合适。 将 Kestrel 用作面向公众的边缘服务器或直接转接 Host 标头时,使用主机筛选中间件设置 AllowedHosts 比较合适。

有关转接头中间件的详细信息,请参阅 配置 ASP.NET Core 以使用代理服务器和负载均衡器

Kestrel 是一个跨平台的适用于 ASP.NET Core 的 Web 服务器 Kestrel 是 Web 服务器,默认包括在 ASP.NET Core 项目模板中。

Kestrel 支持以下方案:

  • HTTPS
  • 用于启用 WebSocket 的不透明升级
  • 用于获得 Nginx 高性能的 Unix 套接字
  • HTTP/2(除 macOS† 以外)

macOS 的未来版本将支持 †HTTP/2。

.NET Core 支持的所有平台和版本均支持 Kestrel。

查看或下载示例代码如何下载

HTTP/2 支持

如果满足以下基本要求,将为 ASP.NET Core 应用提供 HTTP/2

  • 操作系统†
    • Windows Server 2016/Windows 10 或更高版本‡
    • 具有 OpenSSL 1.0.2 或更高版本的 Linux(例如,Ubuntu 16.04 或更高版本)
  • 目标框架:.NET Core 2.2 或更高版本
  • 应用程序层协议协商 (ALPN) 连接
  • TLS 1.2 或更高版本的连接

macOS 的未来版本将支持 †HTTP/2。 ‡Kestrel 在 Windows Server 2012 R2 和 Windows 8.1 上对 HTTP/2 的支持有限。 支持受限是因为可在这些操作系统上使用的受支持 TLS 密码套件列表有限。 可能需要使用椭圆曲线数字签名算法 (ECDSA) 生成的证书来保护 TLS 连接。

如果已建立 HTTP/2 连接,HttpRequest.Protocol 会报告 HTTP/2

默认情况下,禁用 HTTP/2。 有关配置的详细信息,请参阅 Kestrel 选项ListenOptions.Protocols 部分。

何时结合使用 Kestrel 和反向代理

可以单独使用 Kestrel,也可以将其与反向代理服务器(如 Internet Information Services (IIS)NginxApache)结合使用。 反向代理服务器接收来自网络的 HTTP 请求,并将这些请求转发到 Kestrel。

Kestrel 用作边缘(面向 Internet)Web 服务器:

Kestrel 直接与 Internet 通信,不使用反向代理服务器

Kestrel 用于反向代理配置:

Kestrel 通过反向代理服务器(如 IIS、Nginx 或 Apache)间接与 Internet 进行通信

无论配置是否使用反向代理服务器,都是受支持的托管配置。

在没有反向代理服务器的情况下用作边缘服务器的 Kestrel 不支持在多个进程间共享相同的 IP 和端口。 如果将 Kestrel 配置为侦听某个端口,Kestrel 会处理该端口的所有流量(无视请求的 Host 标头)。 可以共享端口的反向代理能在唯一的 IP 和端口上将请求转发至 Kestrel。

即使不需要反向代理服务器,使用反向代理服务器可能也是个不错的选择。

反向代理:

  • 可以限制所承载的应用中的公开的公共外围应用。
  • 提供额外的配置和防护层。
  • 可以更好地与现有基础结构集成。
  • 简化了负载均和和安全通信 (HTTPS) 配置。 仅反向代理服务器需要 X.509 证书,并且该服务器可使用普通 HTTP 在内部网络上与应用服务器通信。

警告

采用反向代理配置进行托管需要主机筛选

如何在 ASP.NET Core 应用中使用 Kestrel

Microsoft.AspNetCore.App 元包中包括 Microsoft.AspNetCore.Server.Kestrel 包。

默认情况下,ASP.NET Core 项目模板使用 Kestrel。 在 Program.cs 中,模板代码调用 CreateDefaultBuilder,后者在后台调用 UseKestrel

public static void Main(string[] args)
{
    CreateWebHostBuilder(args).Build().Run();
}

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>();

有关 CreateDefaultBuilder 和生成主机的详细信息,请参阅 ASP.NET Core Web 主机 的“设置主机”部分。

若要在调用 CreateDefaultBuilder 后提供其他配置,请使用 ConfigureKestrel

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .ConfigureKestrel((context, serverOptions) =>
        {
            // Set properties and call methods on serverOptions
        });

如果应用未调用 CreateDefaultBuilder 来设置主机,请在调用 ConfigureKestrel 之前先调用 UseKestrel

public static void Main(string[] args)
{
    var host = new WebHostBuilder()
        .UseContentRoot(Directory.GetCurrentDirectory())
        .UseKestrel()
        .UseIISIntegration()
        .UseStartup<Startup>()
        .ConfigureKestrel((context, serverOptions) =>
        {
            // Set properties and call methods on serverOptions
        })
        .Build();

    host.Run();
}

Kestrel 选项

Kestrel Web 服务器具有约束配置选项,这些选项在面向 Internet 的部署中尤其有用。

KestrelServerOptions 类的 Limits 属性设置约束。 Limits 属性包含 KestrelServerLimits 类的实例。

下面的示例使用 Microsoft.AspNetCore.Server.Kestrel.Core 命名空间。

using Microsoft.AspNetCore.Server.Kestrel.Core;

Kestrel 选项(已在以下示例的 C# 代码中配置)也可以使用配置提供程序进行设置。 例如,文件配置提供程序可以从 appsettings.json 或 appsettings.{Environment}.json 文件加载 Kestrel 配置:

{
  "Kestrel": {
    "Limits": {
      "MaxConcurrentConnections": 100,
      "MaxConcurrentUpgradedConnections": 100
    }
  }
}

使用以下方法之一:

  • Startup.ConfigureServices 中配置 Kestrel:

    1. IConfiguration 的实例注入到 Startup 类中。 下面的示例假定注入的配置已分配给 Configuration 属性。

    2. Startup.ConfigureServices 中,将配置的 Kestrel 部分加载到 Kestrel 的配置中:

      using Microsoft.Extensions.Configuration
      
      public class Startup
      {
          public Startup(IConfiguration configuration)
          {
              Configuration = configuration;
          }
      
          public IConfiguration Configuration { get; }
      
          public void ConfigureServices(IServiceCollection services)
          {
              services.Configure<KestrelServerOptions>(
                  Configuration.GetSection("Kestrel"));
          }
      
          public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
          {
              ...
          }
      }
      
  • 构建主机时配置 Kestrel:

    在 Program.cs 中,将配置的 Kestrel 部分加载到 Kestrel 的配置中:

    // using Microsoft.Extensions.DependencyInjection;
    
    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .ConfigureServices((context, services) =>
            {
                services.Configure<KestrelServerOptions>(
                    context.Configuration.GetSection("Kestrel"));
            })
            .UseStartup<Startup>();
    

上述两种方法适用于任何配置提供程序

保持活动状态超时

KeepAliveTimeout

获取或设置保持活动状态超时 默认值为 2 分钟。

.ConfigureKestrel((context, serverOptions) =>
{
    serverOptions.Limits.MaxConcurrentConnections = 100;
    serverOptions.Limits.MaxConcurrentUpgradedConnections = 100;
    serverOptions.Limits.MaxRequestBodySize = 10 * 1024;
    serverOptions.Limits.MinRequestBodyDataRate =
        new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(10));
    serverOptions.Limits.MinResponseDataRate =
        new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(10));
    serverOptions.Listen(IPAddress.Loopback, 5000);
    serverOptions.Listen(IPAddress.Loopback, 5001, listenOptions =>
    {
        listenOptions.UseHttps("testCert.pfx", "testPassword");
    });
    serverOptions.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(2);
    serverOptions.Limits.RequestHeadersTimeout = TimeSpan.FromMinutes(1);
});

客户端最大连接数

MaxConcurrentConnections MaxConcurrentUpgradedConnections

可使用以下代码为整个应用设置并发打开的最大 TCP 连接数:

.ConfigureKestrel((context, serverOptions) =>
{
    serverOptions.Limits.MaxConcurrentConnections = 100;
    serverOptions.Limits.MaxConcurrentUpgradedConnections = 100;
    serverOptions.Limits.MaxRequestBodySize = 10 * 1024;
    serverOptions.Limits.MinRequestBodyDataRate =
        new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(10));
    serverOptions.Limits.MinResponseDataRate =
        new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(10));
    serverOptions.Listen(IPAddress.Loopback, 5000);
    serverOptions.Listen(IPAddress.Loopback, 5001, listenOptions =>
    {
        listenOptions.UseHttps("testCert.pfx", "testPassword");
    });
    serverOptions.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(2);
    serverOptions.Limits.RequestHeadersTimeout = TimeSpan.FromMinutes(1);
});

对于已从 HTTP 或 HTTPS 升级到另一个协议(例如,Websocket 请求)的连接,有一个单独的限制。 连接升级后,不会计入 MaxConcurrentConnections 限制。

.ConfigureKestrel((context, serverOptions) =>
{
    serverOptions.Limits.MaxConcurrentConnections = 100;
    serverOptions.Limits.MaxConcurrentUpgradedConnections = 100;
    serverOptions.Limits.MaxRequestBodySize = 10 * 1024;
    serverOptions.Limits.MinRequestBodyDataRate =
        new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(10));
    serverOptions.Limits.MinResponseDataRate =
        new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(10));
    serverOptions.Listen(IPAddress.Loopback, 5000);
    serverOptions.Listen(IPAddress.Loopback, 5001, listenOptions =>
    {
        listenOptions.UseHttps("testCert.pfx", "testPassword");
    });
    serverOptions.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(2);
    serverOptions.Limits.RequestHeadersTimeout = TimeSpan.FromMinutes(1);
});

默认情况下,最大连接数不受限制 (NULL)。

请求正文最大大小

MaxRequestBodySize

默认的请求正文最大大小为 30,000,000 字节,大约 28.6 MB。

在 ASP.NET Core MVC 应用中替代限制的推荐方法是在操作方法上使用 RequestSizeLimitAttribute 属性:

[RequestSizeLimit(100000000)]
public IActionResult MyActionMethod()

以下示例演示如何为每个请求上的应用配置约束:

.ConfigureKestrel((context, serverOptions) =>
{
    serverOptions.Limits.MaxConcurrentConnections = 100;
    serverOptions.Limits.MaxConcurrentUpgradedConnections = 100;
    serverOptions.Limits.MaxRequestBodySize = 10 * 1024;
    serverOptions.Limits.MinRequestBodyDataRate =
        new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(10));
    serverOptions.Limits.MinResponseDataRate =
        new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(10));
    serverOptions.Listen(IPAddress.Loopback, 5000);
    serverOptions.Listen(IPAddress.Loopback, 5001, listenOptions =>
    {
        listenOptions.UseHttps("testCert.pfx", "testPassword");
    });
    serverOptions.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(2);
    serverOptions.Limits.RequestHeadersTimeout = TimeSpan.FromMinutes(1);
});

在中间件中替代特定请求的设置:

app.Run(async (context) =>
{
    context.Features.Get<IHttpMaxRequestBodySizeFeature>()
        .MaxRequestBodySize = 10 * 1024;

    var minRequestRateFeature = 
        context.Features.Get<IHttpMinRequestBodyDataRateFeature>();
    var minResponseRateFeature = 
        context.Features.Get<IHttpMinResponseDataRateFeature>();

    if (minRequestRateFeature != null)
    {
        minRequestRateFeature.MinDataRate = new MinDataRate(
            bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(10));
    }

    if (minResponseRateFeature != null)
    {
        minResponseRateFeature.MinDataRate = new MinDataRate(
            bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(10));
    }

如果应用在开始读取请求后配置请求限制,则会引发异常。 IsReadOnly 属性指示 MaxRequestBodySize 属性处于只读状态,意味已经无法再配置限制。

当应用在 ASP.NET Core 模块后于进程外运行时,由于 IIS 已设置限制,因此禁用了 Kestrel 的请求正文大小限制。

请求正文最小数据速率

MinRequestBodyDataRate MinResponseDataRate

Kestrel 每秒检查一次数据是否以指定的速率(字节/秒)传入。 如果速率低于最小值,则连接超时。宽限期是 Kestrel 提供给客户端用于将其发送速率提升到最小值的时间量;在此期间不会检查速率。 宽限期有助于避免最初由于 TCP 慢启动而以较慢速率发送数据的连接中断。

默认的最小速率为 240 字节/秒,包含 5 秒的宽限期。

最小速率也适用于响应。 除了属性和接口名称中具有 RequestBodyResponse 以外,用于设置请求限制和响应限制的代码相同。

以下示例演示如何在 Program.cs 中配置最小数据速率:

.ConfigureKestrel((context, serverOptions) =>
{
    serverOptions.Limits.MaxConcurrentConnections = 100;
    serverOptions.Limits.MaxConcurrentUpgradedConnections = 100;
    serverOptions.Limits.MaxRequestBodySize = 10 * 1024;
    serverOptions.Limits.MinRequestBodyDataRate =
        new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(10));
    serverOptions.Limits.MinResponseDataRate =
        new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(10));
    serverOptions.Listen(IPAddress.Loopback, 5000);
    serverOptions.Listen(IPAddress.Loopback, 5001, listenOptions =>
    {
        listenOptions.UseHttps("testCert.pfx", "testPassword");
    });
    serverOptions.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(2);
    serverOptions.Limits.RequestHeadersTimeout = TimeSpan.FromMinutes(1);
});

在中间件中替代每个请求的最低速率限制:

app.Run(async (context) =>
{
    context.Features.Get<IHttpMaxRequestBodySizeFeature>()
        .MaxRequestBodySize = 10 * 1024;

    var minRequestRateFeature = 
        context.Features.Get<IHttpMinRequestBodyDataRateFeature>();
    var minResponseRateFeature = 
        context.Features.Get<IHttpMinResponseDataRateFeature>();

    if (minRequestRateFeature != null)
    {
        minRequestRateFeature.MinDataRate = new MinDataRate(
            bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(10));
    }

    if (minResponseRateFeature != null)
    {
        minResponseRateFeature.MinDataRate = new MinDataRate(
            bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(10));
    }

由于协议支持请求多路复用,HTTP/2 不支持基于每个请求修改速率限制,因此 HTTP/2 请求的 HttpContext.Features 中不存在前面示例中引用的速率特性。 通过 KestrelServerOptions.Limits 配置的服务器范围的速率限制仍适用于 HTTP/1.x 和 HTTP/2 连接。

请求标头超时

RequestHeadersTimeout

获取或设置服务器接收请求标头所花费的最大时间量。 默认值为 30 秒。

.ConfigureKestrel((context, serverOptions) =>
{
    serverOptions.Limits.MaxConcurrentConnections = 100;
    serverOptions.Limits.MaxConcurrentUpgradedConnections = 100;
    serverOptions.Limits.MaxRequestBodySize = 10 * 1024;
    serverOptions.Limits.MinRequestBodyDataRate =
        new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(10));
    serverOptions.Limits.MinResponseDataRate =
        new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(10));
    serverOptions.Listen(IPAddress.Loopback, 5000);
    serverOptions.Listen(IPAddress.Loopback, 5001, listenOptions =>
    {
        listenOptions.UseHttps("testCert.pfx", "testPassword");
    });
    serverOptions.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(2);
    serverOptions.Limits.RequestHeadersTimeout = TimeSpan.FromMinutes(1);
});

每个连接的最大流

Http2.MaxStreamsPerConnection 限制每个 HTTP/2 连接的并发请求流的数量。 拒绝过多的流。

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .ConfigureKestrel((context, serverOptions) =>
        {
            serverOptions.Limits.Http2.MaxStreamsPerConnection = 100;
        });

默认值为 100。

标题表大小

HPACK 解码器解压缩 HTTP/2 连接的 HTTP 标头。 Http2.HeaderTableSize 限制 HPACK 解码器使用的标头压缩表的大小。 该值以八位字节提供,且必须大于零 (0)。

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .ConfigureKestrel((context, serverOptions) =>
        {
            serverOptions.Limits.Http2.HeaderTableSize = 4096;
        });

默认值为 4096。

最大帧大小

Http2.MaxFrameSize 指示要接收的 HTTP/2 连接帧有效负载的最大大小。 该值以八位字节提供,必须介于 2^14 (16,384) 和 2^24-1 (16,777,215) 之间。

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .ConfigureKestrel((context, serverOptions) =>
        {
            serverOptions.Limits.Http2.MaxFrameSize = 16384;
        });

默认值为 2^14 (16,384)。

最大请求标头大小

Http2.MaxRequestHeaderFieldSize 表示请求标头值的允许的最大大小(用八进制表示)。 此限制同时适用于压缩和未压缩表示形式中的名称和值。 该值必须大于零 (0)。

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .ConfigureKestrel((context, serverOptions) =>
        {
            serverOptions.Limits.Http2.MaxRequestHeaderFieldSize = 8192;
        });

默认值为 8,192。

初始连接窗口大小

Http2.InitialConnectionWindowSize 表示服务器一次性缓存的最大请求主体数据大小(每次连接时在所有请求(流)中汇总,以字节为单位)。 请求也受 Http2.InitialStreamWindowSize 限制。 该值必须大于或等于 65,535,并小于 2^31 (2,147,483,648)。

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .ConfigureKestrel((context, serverOptions) =>
        {
            serverOptions.Limits.Http2.InitialConnectionWindowSize = 131072;
        });

默认值为 128 KB (131,072)。

初始流窗口大小

Http2.InitialStreamWindowSize 表示服务器针对每个请求(流)的一次性缓存的最大请求主体数据大小(以字节为单位)。 请求也受 Http2.InitialStreamWindowSize 限制。 该值必须大于或等于 65,535,并小于 2^31 (2,147,483,648)。

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .ConfigureKestrel((context, serverOptions) =>
        {
            serverOptions.Limits.Http2.InitialStreamWindowSize = 98304;
        });

默认值为 96 KB (98,304)。

同步 IO

AllowSynchronousIO 控制是否允许对请求和响应使用同步 IO。 默认值为 true

警告

大量的阻止同步 IO 操作可能会导致线程池资源不足,进而导致应用无响应。 仅在使用不支持异步 IO 的库时,才启用 AllowSynchronousIO

下面的示例启用同步 IO:

.ConfigureKestrel((context, serverOptions) =>
{
    serverOptions.AllowSynchronousIO = true;
});

有关其他 Kestrel 选项和限制的信息,请参阅:

终结点配置

默认情况下,ASP.NET Core 绑定到:

  • http://localhost:5000
  • https://localhost:5001(存在本地开发证书时)

使用以下内容指定 URL:

  • ASPNETCORE_URLS 环境变量。
  • --urls 命令行参数。
  • urls 主机配置键。
  • UseUrls 扩展方法。

采用这些方法提供的值可以是一个或多个 HTTP 和 HTTPS 终结点(如果默认证书可用,则为 HTTPS)。 将值配置为以分号分隔的列表(例如 "Urls": "http://localhost:8000;http://localhost:8001")。

有关这些方法的详细信息,请参阅服务器 URL重写配置

关于开发证书的创建:

某些浏览器需要授予显式权限才能信任本地开发证书。

项目模板将应用配置为默认情况下在 HTTPS 上运行,并包括 HTTPS 重定向和 HSTS 支持

调用 KestrelServerOptions 上的 ListenListenUnixSocket 方法以配置 URL 前缀和 Kestrel 的端口。

UseUrls--urls 命令行参数、urls 主机配置键以及 ASPNETCORE_URLS 环境变量也有用,但具有本节后面注明的限制(必须要有可用于 HTTPS 终结点配置的默认证书)。

KestrelServerOptions 配置:

ConfigureEndpointDefaults(Action<ListenOptions>)

指定一个为每个指定的终结点运行的配置 Action 多次调用 ConfigureEndpointDefaults,用最新指定的 Action 替换之前的 Action

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .ConfigureKestrel((context, serverOptions) =>
        {
            serverOptions.ConfigureEndpointDefaults(listenOptions =>
            {
                // Configure endpoint defaults
            });
        });

备注

通过在调用 ConfigureEndpointDefaults 之前调用 Listen 创建的终结点将不会应用默认值。

ConfigureHttpsDefaults(Action<HttpsConnectionAdapterOptions>)

指定一个为每个 HTTPS 终结点运行的配置 Action 多次调用 ConfigureHttpsDefaults,用最新指定的 Action 替换之前的 Action

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .ConfigureKestrel((context, serverOptions) =>
        {
            serverOptions.ConfigureHttpsDefaults(listenOptions =>
            {
                // certificate is an X509Certificate2
                listenOptions.ServerCertificate = certificate;
            });
        });

备注

通过在调用 ConfigureHttpsDefaults 之前调用 Listen 创建的终结点将不会应用默认值。

Configure(IConfiguration)

创建配置加载程序,用于设置将 IConfiguration 作为输入的 Kestrel。 配置必须针对 Kestrel 的配置节。

ListenOptions.UseHttps

将 Kestrel 配置为使用 HTTPS。

ListenOptions.UseHttps 扩展:

  • UseHttps – 将 Kestrel 配置为使用 HTTPS,采用默认证书。 如果没有配置默认证书,则会引发异常。
  • UseHttps(string fileName)
  • UseHttps(string fileName, string password)
  • UseHttps(string fileName, string password, Action<HttpsConnectionAdapterOptions> configureOptions)
  • UseHttps(StoreName storeName, string subject)
  • UseHttps(StoreName storeName, string subject, bool allowInvalid)
  • UseHttps(StoreName storeName, string subject, bool allowInvalid, StoreLocation location)
  • UseHttps(StoreName storeName, string subject, bool allowInvalid, StoreLocation location, Action<HttpsConnectionAdapterOptions> configureOptions)
  • UseHttps(X509Certificate2 serverCertificate)
  • UseHttps(X509Certificate2 serverCertificate, Action<HttpsConnectionAdapterOptions> configureOptions)
  • UseHttps(Action<HttpsConnectionAdapterOptions> configureOptions)

ListenOptions.UseHttps 参数:

  • filename 是证书文件的路径和文件名,关联包含应用内容文件的目录。
  • password 是访问 X.509 证书数据所需的密码。
  • configureOptions 是配置 HttpsConnectionAdapterOptionsAction 返回 ListenOptions
  • storeName 是从中加载证书的证书存储。
  • subject 是证书的主题名称。
  • allowInvalid 指示是否存在需要留意的无效证书,例如自签名证书。
  • location 是从中加载证书的存储位置。
  • serverCertificate 是 X.509 证书。

在生产中,必须显式配置 HTTPS。 至少必须提供默认证书。

下面要描述的支持的配置:

  • 无配置
  • 从配置中替换默认证书
  • 更改代码中的默认值

无配置

Kestrel 在 http://localhost:5000https://localhost:5001 上进行侦听(如果默认证书可用)。

从配置中替换默认证书

CreateDefaultBuilder 在默认情况下调用 Configure(context.Configuration.GetSection("Kestrel")) 来加载 Kestrel 配置。 Kestrel 可以使用默认 HTTPS 应用设置配置架构。 从磁盘上的文件或从证书存储中配置多个终结点,包括要使用的 URL 和证书。

在以下 appsettings.json 示例中:

  • 将 AllowInvalid 设置为 true,从而允许使用无效证书(例如自签名证书)。
  • 任何未指定证书的 HTTPS 终结点(下例中的 HttpsDefaultCert)会回退至在 Certificates > Default 下定义的证书或开发证书。
{
  "Kestrel": {
    "Endpoints": {
      "Http": {
        "Url": "http://localhost:5000"
      },

      "HttpsInlineCertFile": {
        "Url": "https://localhost:5001",
        "Certificate": {
          "Path": "<path to .pfx file>",
          "Password": "<certificate password>"
        }
      },

      "HttpsInlineCertStore": {
        "Url": "https://localhost:5002",
        "Certificate": {
          "Subject": "<subject; required>",
          "Store": "<certificate store; required>",
          "Location": "<location; defaults to CurrentUser>",
          "AllowInvalid": "<true or false; defaults to false>"
        }
      },

      "HttpsDefaultCert": {
        "Url": "https://localhost:5003"
      },

      "Https": {
        "Url": "https://*:5004",
        "Certificate": {
          "Path": "<path to .pfx file>",
          "Password": "<certificate password>"
        }
      }
    },
    "Certificates": {
      "Default": {
        "Path": "<path to .pfx file>",
        "Password": "<certificate password>"
      }
    }
  }
}

此外还可以使用任何证书节点的 Path 和 Password,采用证书存储字段指定证书。 例如,可将 Certificates > Default 证书指定为:

"Default": {
  "Subject": "<subject; required>",
  "Store": "<cert store; required>",
  "Location": "<location; defaults to CurrentUser>",
  "AllowInvalid": "<true or false; defaults to false>"
}

架构的注意事项:

  • 终结点的名称不区分大小写。 例如,HTTPSHttps 都是有效的。
  • 每个终结点都要具备 Url 参数。 此参数的格式和顶层 Urls 配置参数一样,只不过它只能有单个值。
  • 这些终结点不会添加进顶层 Urls 配置中定义的终结点,而是替换它们。 通过 Listen 在代码中定义的终结点与在配置节中定义的终结点相累积。
  • Certificate 部分是可选的。 如果为指定 Certificate 部分,则使用在之前的方案中定义的默认值。 如果没有可用的默认值,服务器会引发异常且无法启动。
  • Certificate 支持 Path–Password 和 Subject–Store 证书。
  • 只要不会导致端口冲突,就能以这种方式定义任何数量的终结点。
  • options.Configure(context.Configuration.GetSection("{SECTION}")) 通过 .Endpoint(string name, listenOptions => { }) 方法返回 KestrelConfigurationLoader,可以用于补充已配置的终结点设置:
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .UseKestrel((context, serverOptions) =>
        {
            serverOptions.Configure(context.Configuration.GetSection("Kestrel"))
                .Endpoint("HTTPS", listenOptions =>
                {
                    listenOptions.HttpsOptions.SslProtocols = SslProtocols.Tls12;
                });
        });

可以直接访问 KestrelServerOptions.ConfigurationLoader 以继续迭代现有加载程序,例如由 CreateDefaultBuilder 提供的加载程序。

  • 每个终结点的配置节都可用于 Endpoint 方法中的选项,以便读取自定义设置。
  • 通过另一节再次调用 options.Configure(context.Configuration.GetSection("{SECTION}")) 可能加载多个配置。 只使用最新配置,除非之前的实例上显式调用了 Load 元包不会调用 Load,所以可能会替换它的默认配置节。
  • KestrelConfigurationLoaderKestrelServerOptions 将 API 的 Listen 簇反射为 Endpoint 重载,因此可在同样的位置配置代码和配置终结点。 这些重载不使用名称,且只使用配置中的默认设置。

更改代码中的默认值

可以使用 ConfigureEndpointDefaultsConfigureHttpsDefaults 更改 ListenOptionsHttpsConnectionAdapterOptions 的默认设置,包括重写之前的方案指定的默认证书。 需要在配置任何终结点之前调用 ConfigureEndpointDefaultsConfigureHttpsDefaults

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .UseKestrel((context, serverOptions) =>
        {
            serverOptions.ConfigureEndpointDefaults(listenOptions =>
            {
                // Configure endpoint defaults
            });

            serverOptions.ConfigureHttpsDefaults(listenOptions =>
            {
                listenOptions.SslProtocols = SslProtocols.Tls12;
            });
        });

SNI 的 Kestrel 支持

服务器名称指示 (SNI) 可用于承载相同 IP 地址和端口上的多个域。 为了运行 SNI,客户端在 TLS 握手过程中将进行安全会话的主机名发送至服务器,从而让服务器可以提供正确的证书。 在 TLS 握手后的安全会话期间,客户端将服务器提供的证书用于与服务器进行加密通信。

Kestrel 通过 ServerCertificateSelector 回调支持 SNI。 每次连接调用一次回调,从而允许应用检查主机名并选择合适的证书。

SNI 支持要求:

  • 在目标框架 netcoreapp2.1 或更高版本上运行。 net461 或最高版本上,将调用回调,但是 name 始终为 null 如果客户端未在 TLS 握手过程中提供主机名参数,则 name 也为 null
  • 所有网站在相同的 Kestrel 实例上运行。 Kestrel 在无反向代理时不支持跨多个实例共享一个 IP 地址和端口。
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .ConfigureKestrel((context, serverOptions) =>
        {
            serverOptions.ListenAnyIP(5005, listenOptions =>
            {
                listenOptions.UseHttps(httpsOptions =>
                {
                    var localhostCert = CertificateLoader.LoadFromStoreCert(
                        "localhost", "My", StoreLocation.CurrentUser,
                        allowInvalid: true);
                    var exampleCert = CertificateLoader.LoadFromStoreCert(
                        "example.com", "My", StoreLocation.CurrentUser,
                        allowInvalid: true);
                    var subExampleCert = CertificateLoader.LoadFromStoreCert(
                        "sub.example.com", "My", StoreLocation.CurrentUser,
                        allowInvalid: true);
                    var certs = new Dictionary<string, X509Certificate2>(
                        StringComparer.OrdinalIgnoreCase);
                    certs["localhost"] = localhostCert;
                    certs["example.com"] = exampleCert;
                    certs["sub.example.com"] = subExampleCert;

                    httpsOptions.ServerCertificateSelector = (connectionContext, name) =>
                    {
                        if (name != null && certs.TryGetValue(name, out var cert))
                        {
                            return cert;
                        }

                        return exampleCert;
                    };
                });
            });
        });

连接日志记录

调用 UseConnectionLogging 以发出用于进行连接上的字节级别通信的调试级别日志。 连接日志记录有助于排查低级通信中的问题,例如在 TLS 加密期间和代理后。 如果 UseConnectionLogging 放置在 UseHttps 之前,则会记录加密的流量。 如果 UseConnectionLogging 放置于 UseHttps 之后,则会记录解密的流量。

webBuilder.ConfigureKestrel(serverOptions =>
{
    serverOptions.Listen(IPAddress.Any, 8000, listenOptions =>
    {
        listenOptions.UseConnectionLogging();
    });
});

绑定到 TCP 套接字

Listen 方法绑定至 TCP 套接字,且 options lambda 允许 X.509 证书配置:

public static void Main(string[] args)
{
    CreateWebHostBuilder(args).Build().Run();
}

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .ConfigureKestrel((context, serverOptions) =>
        {
            serverOptions.Listen(IPAddress.Loopback, 5000);
            serverOptions.Listen(IPAddress.Loopback, 5001, listenOptions =>
            {
                listenOptions.UseHttps("testCert.pfx", "testPassword");
            });
        });

示例使用 ListenOptions 为终结点配置 HTTPS。 可使用相同 API 为特定终结点配置其他 Kestrel 设置。

在 Windows 上,可以使用 New-SelfSignedCertificate PowerShell cmdlet 创建自签名证书。 有关不支持的示例,请参阅 UpdateIISExpressSSLForChrome.ps1

在 macOS、Linux 和 Windows 上,可以使用 OpenSSL 创建证书。

绑定到 Unix 套接字

可通过 ListenUnixSocket 侦听 Unix 套接字以提高 Nginx 的性能,如以下示例所示:

.ConfigureKestrel((context, serverOptions) =>
{
    serverOptions.ListenUnixSocket("/tmp/kestrel-test.sock");
    serverOptions.ListenUnixSocket("/tmp/kestrel-test.sock", listenOptions =>
    {
        listenOptions.UseHttps("testCert.pfx", "testpassword");
    });
});
  • 在 Nginx confiuguration 文件中,将 server > location > proxy_pass 条目设置为 http://unix:/tmp/{KESTREL SOCKET}:/; {KESTREL SOCKET} 是提供给 ListenUnixSocket 的套接字的名称(例如,上述示例中的 kestrel-test.sock)。
  • 确保套接字可由 Nginx (例如 chmod go+w /tmp/kestrel-test.sock)进行写入。

端口 0

如果指定端口号 0,Kestrel 将动态绑定到可用端口。 以下示例演示如何确定 Kestrel 在运行时实际绑定到的端口:

public void Configure(IApplicationBuilder app)
{
    var serverAddressesFeature = 
        app.ServerFeatures.Get<IServerAddressesFeature>();

    app.UseStaticFiles();

    app.Run(async (context) =>
    {
        context.Response.ContentType = "text/html";
        await context.Response
            .WriteAsync("<!DOCTYPE html><html lang=\"en\"><head>" +
                "<title></title></head><body><p>Hosted by Kestrel</p>");

        if (serverAddressesFeature != null)
        {
            await context.Response
                .WriteAsync("<p>Listening on the following addresses: " +
                    string.Join(", ", serverAddressesFeature.Addresses) +
                    "</p>");
        }

        await context.Response.WriteAsync("<p>Request URL: " +
            $"{context.Request.GetDisplayUrl()}<p>");
    });
}

在应用运行时,控制台窗口输出指示可用于访问应用的动态端口:

Listening on the following addresses: http://127.0.0.1:48508

限制

使用以下方法配置终结点:

  • UseUrls
  • --urls 命令行参数
  • urls 主机配置键
  • ASPNETCORE_URLS 环境变量

若要将代码用于 Kestrel 以外的服务器,这些方法非常有用。 不过,请注意以下限制:

  • HTTPS 无法与这些方法结合使用,除非在 HTTPS 终结点配置中提供了默认证书(例如,使用 KestrelServerOptions 配置或配置文件,如本主题前面的部分所示)。
  • 如果同时使用 ListenUseUrls 方法,Listen 终结点将覆盖 UseUrls 终结点。

IIS 终结点配置

使用 IIS 时,由 ListenUseUrls 设置用于 IIS 覆盖绑定的 URL 绑定。 有关详细信息,请参阅 ASP.NET Core 模块主题。

ListenOptions.Protocols

Protocols 属性建立在连接终结点上或为服务器启用的 HTTP 协议(HttpProtocols)。 HttpProtocols 枚举向 Protocols 属性赋值。

HttpProtocols 枚举值 允许的连接协议
Http1 仅 HTTP/1.1。 可以在具有 TLS 或没有 TLS 的情况下使用。
Http2 仅 HTTP/2。 仅当客户端支持先验知识模式时,才可以在没有 TLS 的情况下使用。
Http1AndHttp2 HTTP/1.1 和 HTTP/2。 HTTP/2 需要 TLS 和应用程序层协议协商 (ALPN) 连接;否则,连接默认为 HTTP/1.1。

默认协议是 HTTP/1.1。

HTTP/2 的 TLS 限制:

  • TLS 版本 1.2 或更高版本
  • 重新协商已禁用
  • 压缩已禁用
  • 最小的临时密钥交换大小:
    • 椭圆曲线 Diffie-Hellman (ECDHE) [RFC4492] – 最小 224 位
    • 有限字段 Diffie-Hellman (DHE) [TLS12] – 最小 2048 位
  • 密码套件未列入阻止列表

默认情况下,支持具有 P-256 椭圆曲线 [FIPS186] 的 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 [TLS-ECDHE]。

以下示例允许端口 8000 上的 HTTP/1.1 和 HTTP/2 连接。 TLS 使用提供的证书来保护连接:

.ConfigureKestrel((context, serverOptions) =>
{
    serverOptions.Listen(IPAddress.Any, 8000, listenOptions =>
    {
        listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
        listenOptions.UseHttps("testCert.pfx", "testPassword");
    });
});

(可选)创建 IConnectionAdapter 实现,以针对特定密码的每个连接筛选 TLS 握手:

.ConfigureKestrel((context, serverOptions) =>
{
    serverOptions.Listen(IPAddress.Any, 8000, listenOptions =>
    {
        listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
        listenOptions.UseHttps("testCert.pfx", "testPassword");
        listenOptions.ConnectionAdapters.Add(new TlsFilterAdapter());
    });
});
private class TlsFilterAdapter : IConnectionAdapter
{
    public bool IsHttps => false;

    public Task<IAdaptedConnection> OnConnectionAsync(ConnectionAdapterContext context)
    {
        var tlsFeature = context.Features.Get<ITlsHandshakeFeature>();

        // Throw NotSupportedException for any cipher algorithm that the app doesn't
        // wish to support. Alternatively, define and compare
        // ITlsHandshakeFeature.CipherAlgorithm to a list of acceptable cipher
        // suites.
        //
        // No encryption is used with a CipherAlgorithmType.Null cipher algorithm.
        if (tlsFeature.CipherAlgorithm == CipherAlgorithmType.Null)
        {
            throw new NotSupportedException("Prohibited cipher: " + tlsFeature.CipherAlgorithm);
        }

        return Task.FromResult<IAdaptedConnection>(new AdaptedConnection(context.ConnectionStream));
    }

    private class AdaptedConnection : IAdaptedConnection
    {
        public AdaptedConnection(Stream adaptedStream)
        {
            ConnectionStream = adaptedStream;
        }

        public Stream ConnectionStream { get; }

        public void Dispose()
        {
        }
    }
}

从配置中设置协议

CreateDefaultBuilder 在默认情况下调用 serverOptions.Configure(context.Configuration.GetSection("Kestrel")) 来加载 Kestrel 配置。

在以下 appsettings.json 示例中,为 Kestrel 的所有终结点建立默认的连接协议(HTTP/1.1 和 HTTP/2):

{
  "Kestrel": {
    "EndpointDefaults": {
      "Protocols": "Http1AndHttp2"
    }
  }
}

以下配置文件示例为特定终结点建立了连接协议:

{
  "Kestrel": {
    "Endpoints": {
      "HttpsDefaultCert": {
        "Url": "https://localhost:5001",
        "Protocols": "Http1AndHttp2"
      }
    }
  }
}

代码中指定的协议覆盖了由配置设置的值。

传输配置

对于 ASP.NET Core 2.1 版,Kestrel 默认传输不再基于 Libuv,而是基于托管的套接字。 这是 ASP.NET Core 2.0 应用升级到 2.1 时的一个重大更改,它调用 UseLibuv 并依赖于以下包中的一个:

对于需要使用 Libuv 的项目:

  • 将用于 Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv 包的依赖项添加到应用的项目文件:

    <PackageReference Include="Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv"
                      Version="{VERSION}" />
    
  • 调用 UseLibuv

    public class Program
    {
        public static void Main(string[] args)
        {
            CreateWebHostBuilder(args).Build().Run();
        }
    
        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseLibuv()
                .UseStartup<Startup>();
    }
    

URL 前缀

如果使用 UseUrls--urls 命令行参数、urls 主机配置键或 ASPNETCORE_URLS 环境变量,URL 前缀可采用以下任意格式。

仅 HTTP URL 前缀是有效的。 使用 UseUrls 配置 URL 绑定时,Kestrel 不支持 HTTPS。

  • 包含端口号的 IPv4 地址

    http://65.55.39.10:80/
    

    0.0.0.0 是一种绑定到所有 IPv4 地址的特殊情况。

  • 包含端口号的 IPv6 地址

    http://[0:0:0:0:0:ffff:4137:270a]:80/
    

    [::] 是 IPv4 0.0.0.0 的 IPv6 等效项。

  • 包含端口号的主机名

    http://contoso.com:80/
    http://*:80/
    

    主机名、*+ 并不特殊。 没有识别为有效 IP 地址或 localhost 的任何内容都将绑定到所有 IPv4 和 IPv6 IP。 若要将不同主机名绑定到相同端口上的不同 ASP.NET Core 应用,请使用 HTTP.sys 或 IIS、Nginx 或 Apache 等反向代理服务器。

    警告

    采用反向代理配置进行托管需要主机筛选

  • 包含端口号的主机 localhost 名称或包含端口号的环回 IP

    http://localhost:5000/
    http://127.0.0.1:5000/
    http://[::1]:5000/
    

    指定 localhost 后,Kestrel 将尝试绑定到 IPv4 和 IPv6 环回接口。 如果其他服务正在任一环回接口上使用请求的端口,则 Kestrel 将无法启动。 如果任一环回接口出于任何其他原因(通常是因为 IPv6 不受支持)而不可用,则 Kestrel 将记录一个警告。

主机筛选

尽管 Kestrel 支持基于前缀的配置(例如 http://example.com:5000),但 Kestrel 在很大程度上会忽略主机名。 主机 localhost 是一个特殊情况,用于绑定至环回地址。 除了显式 IP 地址以外的所有主机都绑定至所有公共 IP 地址。 不验证 Host 标头。

解决方法是,使用主机筛选中间件。 主机筛选中间件由 Microsoft.AspNetCore.HostFiltering 包提供,此包包含在 Microsoft.AspNetCore.App 元包中(ASP.NET Core 2.1 或 2.2)。 由调用 AddHostFilteringCreateDefaultBuilder 添加中间件:

public class Program
{
    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>();
}

默认情况下,主机筛选中间件处于禁用状态。 要启用该中间件,请在 appsettings.json/appsettings.<EnvironmentName>.json 中定义一个 AllowedHosts 键。 此值是以分号分隔的不带端口号的主机名列表:

appsettings.json:

{
  "AllowedHosts": "example.com;localhost"
}

备注

转接头中间件 同样包含 AllowedHosts 选项。 转接头中间件和主机筛选中间件具有适合不同方案的相似功能。 如果未保留 Host 标头,并且使用反向代理服务器或负载均衡器转接请求,则使用转接头中间件设置 AllowedHosts 比较合适。 将 Kestrel 用作面向公众的边缘服务器或直接转接 Host 标头时,使用主机筛选中间件设置 AllowedHosts 比较合适。

有关转接头中间件的详细信息,请参阅 配置 ASP.NET Core 以使用代理服务器和负载均衡器

Kestrel 是一个跨平台的适用于 ASP.NET Core 的 Web 服务器 Kestrel 是 Web 服务器,默认包括在 ASP.NET Core 项目模板中。

Kestrel 支持以下方案:

  • HTTPS
  • 用于启用 WebSocket 的不透明升级
  • 用于获得 Nginx 高性能的 Unix 套接字

.NET Core 支持的所有平台和版本均支持 Kestrel。

查看或下载示例代码如何下载

何时结合使用 Kestrel 和反向代理

可以单独使用 Kestrel,也可以将其与反向代理服务器(如 Internet Information Services (IIS)NginxApache)结合使用。 反向代理服务器接收来自网络的 HTTP 请求,并将这些请求转发到 Kestrel。

Kestrel 用作边缘(面向 Internet)Web 服务器:

Kestrel 直接与 Internet 通信,不使用反向代理服务器

Kestrel 用于反向代理配置:

Kestrel 通过反向代理服务器(如 IIS、Nginx 或 Apache)间接与 Internet 进行通信

无论配置是否使用反向代理服务器,都是受支持的托管配置。

在没有反向代理服务器的情况下用作边缘服务器的 Kestrel 不支持在多个进程间共享相同的 IP 和端口。 如果将 Kestrel 配置为侦听某个端口,Kestrel 会处理该端口的所有流量(无视请求的 Host 标头)。 可以共享端口的反向代理能在唯一的 IP 和端口上将请求转发至 Kestrel。

即使不需要反向代理服务器,使用反向代理服务器可能也是个不错的选择。

反向代理:

  • 可以限制所承载的应用中的公开的公共外围应用。
  • 提供额外的配置和防护层。
  • 可以更好地与现有基础结构集成。
  • 简化了负载均和和安全通信 (HTTPS) 配置。 仅反向代理服务器需要 X.509 证书,并且该服务器可使用普通 HTTP 在内部网络上与应用服务器通信。

警告

采用反向代理配置进行托管需要主机筛选

如何在 ASP.NET Core 应用中使用 Kestrel

Microsoft.AspNetCore.App 元包中包括 Microsoft.AspNetCore.Server.Kestrel 包。

默认情况下,ASP.NET Core 项目模板使用 Kestrel。 在 Program.cs 中,模板代码调用 CreateDefaultBuilder,后者在后台调用 UseKestrel

若要在调用 CreateDefaultBuilder 后提供其他配置,请调用 UseKestrel

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .UseKestrel(serverOptions =>
        {
            // Set properties and call methods on serverOptions
        });

有关 CreateDefaultBuilder 和生成主机的详细信息,请参阅 ASP.NET Core Web 主机 的“设置主机”部分。

Kestrel 选项

Kestrel Web 服务器具有约束配置选项,这些选项在面向 Internet 的部署中尤其有用。

KestrelServerOptions 类的 Limits 属性设置约束。 Limits 属性包含 KestrelServerLimits 类的实例。

下面的示例使用 Microsoft.AspNetCore.Server.Kestrel.Core 命名空间。

using Microsoft.AspNetCore.Server.Kestrel.Core;

Kestrel 选项(已在以下示例的 C# 代码中配置)也可以使用配置提供程序进行设置。 例如,文件配置提供程序可以从 appsettings.json 或 appsettings.{Environment}.json 文件加载 Kestrel 配置:

{
  "Kestrel": {
    "Limits": {
      "MaxConcurrentConnections": 100,
      "MaxConcurrentUpgradedConnections": 100
    }
  }
}

使用以下方法之一:

  • Startup.ConfigureServices 中配置 Kestrel:

    1. IConfiguration 的实例注入到 Startup 类中。 下面的示例假定注入的配置已分配给 Configuration 属性。

    2. Startup.ConfigureServices 中,将配置的 Kestrel 部分加载到 Kestrel 的配置中:

      using Microsoft.Extensions.Configuration
      
      public class Startup
      {
          public Startup(IConfiguration configuration)
          {
              Configuration = configuration;
          }
      
          public IConfiguration Configuration { get; }
      
          public void ConfigureServices(IServiceCollection services)
          {
              services.Configure<KestrelServerOptions>(
                  Configuration.GetSection("Kestrel"));
          }
      
          public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
          {
              ...
          }
      }
      
  • 构建主机时配置 Kestrel:

    在 Program.cs 中,将配置的 Kestrel 部分加载到 Kestrel 的配置中:

    // using Microsoft.Extensions.DependencyInjection;
    
    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .ConfigureServices((context, services) =>
            {
                services.Configure<KestrelServerOptions>(
                    context.Configuration.GetSection("Kestrel"));
            })
            .UseStartup<Startup>();
    

上述两种方法适用于任何配置提供程序

保持活动状态超时

KeepAliveTimeout

获取或设置保持活动状态超时 默认值为 2 分钟。

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .UseKestrel(serverOptions =>
        {
            serverOptions.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(2);
        });

客户端最大连接数

MaxConcurrentConnections MaxConcurrentUpgradedConnections

可使用以下代码为整个应用设置并发打开的最大 TCP 连接数:

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .UseKestrel(serverOptions =>
        {
            serverOptions.Limits.MaxConcurrentConnections = 100;
        });

对于已从 HTTP 或 HTTPS 升级到另一个协议(例如,Websocket 请求)的连接,有一个单独的限制。 连接升级后,不会计入 MaxConcurrentConnections 限制。

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .UseKestrel(serverOptions =>
        {
            serverOptions.Limits.MaxConcurrentUpgradedConnections = 100;
        });

默认情况下,最大连接数不受限制 (NULL)。

请求正文最大大小

MaxRequestBodySize

默认的请求正文最大大小为 30,000,000 字节,大约 28.6 MB。

在 ASP.NET Core MVC 应用中替代限制的推荐方法是在操作方法上使用 RequestSizeLimitAttribute 属性:

[RequestSizeLimit(100000000)]
public IActionResult MyActionMethod()

以下示例演示如何为每个请求上的应用配置约束:

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .UseKestrel(serverOptions =>
        {
            serverOptions.Limits.MaxRequestBodySize = 10 * 1024;
        });

在中间件中替代特定请求的设置:

app.Run(async (context) =>
{
    context.Features.Get<IHttpMaxRequestBodySizeFeature>()
        .MaxRequestBodySize = 10 * 1024;

    var minRequestRateFeature = 
        context.Features.Get<IHttpMinRequestBodyDataRateFeature>();
    var minResponseRateFeature = 
        context.Features.Get<IHttpMinResponseDataRateFeature>();

    if (minRequestRateFeature != null)
    {
        minRequestRateFeature.MinDataRate = new MinDataRate(
            bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(10));
    }

    if (minResponseRateFeature != null)
    {
        minResponseRateFeature.MinDataRate = new MinDataRate(
            bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(10));
    }

如果应用在开始读取请求后配置请求限制,则会引发异常。 IsReadOnly 属性指示 MaxRequestBodySize 属性处于只读状态,意味已经无法再配置限制。

当应用在 ASP.NET Core 模块后于进程外运行时,由于 IIS 已设置限制,因此禁用了 Kestrel 的请求正文大小限制。

请求正文最小数据速率

MinRequestBodyDataRate MinResponseDataRate

Kestrel 每秒检查一次数据是否以指定的速率(字节/秒)传入。 如果速率低于最小值,则连接超时。宽限期是 Kestrel 提供给客户端用于将其发送速率提升到最小值的时间量;在此期间不会检查速率。 宽限期有助于避免最初由于 TCP 慢启动而以较慢速率发送数据的连接中断。

默认的最小速率为 240 字节/秒,包含 5 秒的宽限期。

最小速率也适用于响应。 除了属性和接口名称中具有 RequestBodyResponse 以外,用于设置请求限制和响应限制的代码相同。

以下示例演示如何在 Program.cs 中配置最小数据速率:

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .UseKestrel(serverOptions =>
        {
            serverOptions.Limits.MinRequestBodyDataRate =
                new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(10));
            serverOptions.Limits.MinResponseDataRate =
                new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(10));
        });

请求标头超时

RequestHeadersTimeout

获取或设置服务器接收请求标头所花费的最大时间量。 默认值为 30 秒。

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .UseKestrel(serverOptions =>
        {
            serverOptions.Limits.RequestHeadersTimeout = TimeSpan.FromMinutes(1);
        });

同步 IO

AllowSynchronousIO 控制是否允许对请求和响应使用同步 IO。 默认值为 true

警告

大量的阻止同步 IO 操作可能会导致线程池资源不足,进而导致应用无响应。 仅在使用不支持异步 IO 的库时,才启用 AllowSynchronousIO

下面的示例禁用同步 IO:

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .UseKestrel(serverOptions =>
        {
            serverOptions.AllowSynchronousIO = false;
        });

有关其他 Kestrel 选项和限制的信息,请参阅:

终结点配置

默认情况下,ASP.NET Core 绑定到:

  • http://localhost:5000
  • https://localhost:5001(存在本地开发证书时)

使用以下内容指定 URL:

  • ASPNETCORE_URLS 环境变量。
  • --urls 命令行参数。
  • urls 主机配置键。
  • UseUrls 扩展方法。

采用这些方法提供的值可以是一个或多个 HTTP 和 HTTPS 终结点(如果默认证书可用,则为 HTTPS)。 将值配置为以分号分隔的列表(例如 "Urls": "http://localhost:8000;http://localhost:8001")。

有关这些方法的详细信息,请参阅服务器 URL重写配置

关于开发证书的创建:

某些浏览器需要授予显式权限才能信任本地开发证书。

项目模板将应用配置为默认情况下在 HTTPS 上运行,并包括 HTTPS 重定向和 HSTS 支持

调用 KestrelServerOptions 上的 ListenListenUnixSocket 方法以配置 URL 前缀和 Kestrel 的端口。

UseUrls--urls 命令行参数、urls 主机配置键以及 ASPNETCORE_URLS 环境变量也有用,但具有本节后面注明的限制(必须要有可用于 HTTPS 终结点配置的默认证书)。

KestrelServerOptions 配置:

ConfigureEndpointDefaults(Action<ListenOptions>)

指定一个为每个指定的终结点运行的配置 Action 多次调用 ConfigureEndpointDefaults,用最新指定的 Action 替换之前的 Action

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .ConfigureKestrel((context, serverOptions) =>
        {
            serverOptions.ConfigureEndpointDefaults(listenOptions =>
            {
                // Configure endpoint defaults
            });
        });

备注

通过在调用 ConfigureEndpointDefaults 之前调用 Listen 创建的终结点将不会应用默认值。

ConfigureHttpsDefaults(Action<HttpsConnectionAdapterOptions>)

指定一个为每个 HTTPS 终结点运行的配置 Action 多次调用 ConfigureHttpsDefaults,用最新指定的 Action 替换之前的 Action

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .ConfigureKestrel((context, serverOptions) =>
        {
            serverOptions.ConfigureHttpsDefaults(listenOptions =>
            {
                // certificate is an X509Certificate2
                listenOptions.ServerCertificate = certificate;
            });
        });

备注

通过在调用 ConfigureHttpsDefaults 之前调用 Listen 创建的终结点将不会应用默认值。

Configure(IConfiguration)

创建配置加载程序,用于设置将 IConfiguration 作为输入的 Kestrel。 配置必须针对 Kestrel 的配置节。

ListenOptions.UseHttps

将 Kestrel 配置为使用 HTTPS。

ListenOptions.UseHttps 扩展:

  • UseHttps – 将 Kestrel 配置为使用 HTTPS,采用默认证书。 如果没有配置默认证书,则会引发异常。
  • UseHttps(string fileName)
  • UseHttps(string fileName, string password)
  • UseHttps(string fileName, string password, Action<HttpsConnectionAdapterOptions> configureOptions)
  • UseHttps(StoreName storeName, string subject)
  • UseHttps(StoreName storeName, string subject, bool allowInvalid)
  • UseHttps(StoreName storeName, string subject, bool allowInvalid, StoreLocation location)
  • UseHttps(StoreName storeName, string subject, bool allowInvalid, StoreLocation location, Action<HttpsConnectionAdapterOptions> configureOptions)
  • UseHttps(X509Certificate2 serverCertificate)
  • UseHttps(X509Certificate2 serverCertificate, Action<HttpsConnectionAdapterOptions> configureOptions)
  • UseHttps(Action<HttpsConnectionAdapterOptions> configureOptions)

ListenOptions.UseHttps 参数:

  • filename 是证书文件的路径和文件名,关联包含应用内容文件的目录。
  • password 是访问 X.509 证书数据所需的密码。
  • configureOptions 是配置 HttpsConnectionAdapterOptionsAction 返回 ListenOptions
  • storeName 是从中加载证书的证书存储。
  • subject 是证书的主题名称。
  • allowInvalid 指示是否存在需要留意的无效证书,例如自签名证书。
  • location 是从中加载证书的存储位置。
  • serverCertificate 是 X.509 证书。

在生产中,必须显式配置 HTTPS。 至少必须提供默认证书。

下面要描述的支持的配置:

  • 无配置
  • 从配置中替换默认证书
  • 更改代码中的默认值

无配置

Kestrel 在 http://localhost:5000https://localhost:5001 上进行侦听(如果默认证书可用)。

从配置中替换默认证书

CreateDefaultBuilder 在默认情况下调用 Configure(context.Configuration.GetSection("Kestrel")) 来加载 Kestrel 配置。 Kestrel 可以使用默认 HTTPS 应用设置配置架构。 从磁盘上的文件或从证书存储中配置多个终结点,包括要使用的 URL 和证书。

在以下 appsettings.json 示例中:

  • 将 AllowInvalid 设置为 true,从而允许使用无效证书(例如自签名证书)。
  • 任何未指定证书的 HTTPS 终结点(下例中的 HttpsDefaultCert)会回退至在 Certificates > Default 下定义的证书或开发证书。
{
  "Kestrel": {
    "Endpoints": {
      "Http": {
        "Url": "http://localhost:5000"
      },

      "HttpsInlineCertFile": {
        "Url": "https://localhost:5001",
        "Certificate": {
          "Path": "<path to .pfx file>",
          "Password": "<certificate password>"
        }
      },

      "HttpsInlineCertStore": {
        "Url": "https://localhost:5002",
        "Certificate": {
          "Subject": "<subject; required>",
          "Store": "<certificate store; required>",
          "Location": "<location; defaults to CurrentUser>",
          "AllowInvalid": "<true or false; defaults to false>"
        }
      },

      "HttpsDefaultCert": {
        "Url": "https://localhost:5003"
      },

      "Https": {
        "Url": "https://*:5004",
        "Certificate": {
          "Path": "<path to .pfx file>",
          "Password": "<certificate password>"
        }
      }
    },
    "Certificates": {
      "Default": {
        "Path": "<path to .pfx file>",
        "Password": "<certificate password>"
      }
    }
  }
}

此外还可以使用任何证书节点的 Path 和 Password,采用证书存储字段指定证书。 例如,可将 Certificates > Default 证书指定为:

"Default": {
  "Subject": "<subject; required>",
  "Store": "<cert store; required>",
  "Location": "<location; defaults to CurrentUser>",
  "AllowInvalid": "<true or false; defaults to false>"
}

架构的注意事项:

  • 终结点的名称不区分大小写。 例如,HTTPSHttps 都是有效的。
  • 每个终结点都要具备 Url 参数。 此参数的格式和顶层 Urls 配置参数一样,只不过它只能有单个值。
  • 这些终结点不会添加进顶层 Urls 配置中定义的终结点,而是替换它们。 通过 Listen 在代码中定义的终结点与在配置节中定义的终结点相累积。
  • Certificate 部分是可选的。 如果为指定 Certificate 部分,则使用在之前的方案中定义的默认值。 如果没有可用的默认值,服务器会引发异常且无法启动。
  • Certificate 支持 Path–Password 和 Subject–Store 证书。
  • 只要不会导致端口冲突,就能以这种方式定义任何数量的终结点。
  • options.Configure(context.Configuration.GetSection("{SECTION}")) 通过 .Endpoint(string name, listenOptions => { }) 方法返回 KestrelConfigurationLoader,可以用于补充已配置的终结点设置:
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .UseKestrel((context, serverOptions) =>
        {
            serverOptions.Configure(context.Configuration.GetSection("Kestrel"))
                .Endpoint("HTTPS", listenOptions =>
                {
                    listenOptions.HttpsOptions.SslProtocols = SslProtocols.Tls12;
                });
        });

可以直接访问 KestrelServerOptions.ConfigurationLoader 以继续迭代现有加载程序,例如由 CreateDefaultBuilder 提供的加载程序。

  • 每个终结点的配置节都可用于 Endpoint 方法中的选项,以便读取自定义设置。
  • 通过另一节再次调用 options.Configure(context.Configuration.GetSection("{SECTION}")) 可能加载多个配置。 只使用最新配置,除非之前的实例上显式调用了 Load 元包不会调用 Load,所以可能会替换它的默认配置节。
  • KestrelConfigurationLoaderKestrelServerOptions 将 API 的 Listen 簇反射为 Endpoint 重载,因此可在同样的位置配置代码和配置终结点。 这些重载不使用名称,且只使用配置中的默认设置。

更改代码中的默认值

可以使用 ConfigureEndpointDefaultsConfigureHttpsDefaults 更改 ListenOptionsHttpsConnectionAdapterOptions 的默认设置,包括重写之前的方案指定的默认证书。 需要在配置任何终结点之前调用 ConfigureEndpointDefaultsConfigureHttpsDefaults

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .UseKestrel((context, serverOptions) =>
        {
            serverOptions.ConfigureEndpointDefaults(listenOptions =>
            {
                // Configure endpoint defaults
            });

            serverOptions.ConfigureHttpsDefaults(listenOptions =>
            {
                listenOptions.SslProtocols = SslProtocols.Tls12;
            });
        });

SNI 的 Kestrel 支持

服务器名称指示 (SNI) 可用于承载相同 IP 地址和端口上的多个域。 为了运行 SNI,客户端在 TLS 握手过程中将进行安全会话的主机名发送至服务器,从而让服务器可以提供正确的证书。 在 TLS 握手后的安全会话期间,客户端将服务器提供的证书用于与服务器进行加密通信。

Kestrel 通过 ServerCertificateSelector 回调支持 SNI。 每次连接调用一次回调,从而允许应用检查主机名并选择合适的证书。

SNI 支持要求:

  • 在目标框架 netcoreapp2.1 或更高版本上运行。 net461 或最高版本上,将调用回调,但是 name 始终为 null 如果客户端未在 TLS 握手过程中提供主机名参数,则 name 也为 null
  • 所有网站在相同的 Kestrel 实例上运行。 Kestrel 在无反向代理时不支持跨多个实例共享一个 IP 地址和端口。
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .UseKestrel((context, serverOptions) =>
        {
            serverOptions.ListenAnyIP(5005, listenOptions =>
            {
                listenOptions.UseHttps(httpsOptions =>
                {
                    var localhostCert = CertificateLoader.LoadFromStoreCert(
                        "localhost", "My", StoreLocation.CurrentUser,
                        allowInvalid: true);
                    var exampleCert = CertificateLoader.LoadFromStoreCert(
                        "example.com", "My", StoreLocation.CurrentUser,
                        allowInvalid: true);
                    var subExampleCert = CertificateLoader.LoadFromStoreCert(
                        "sub.example.com", "My", StoreLocation.CurrentUser,
                        allowInvalid: true);
                    var certs = new Dictionary<string, X509Certificate2>(
                        StringComparer.OrdinalIgnoreCase);
                    certs["localhost"] = localhostCert;
                    certs["example.com"] = exampleCert;
                    certs["sub.example.com"] = subExampleCert;

                    httpsOptions.ServerCertificateSelector = (connectionContext, name) =>
                    {
                        if (name != null && certs.TryGetValue(name, out var cert))
                        {
                            return cert;
                        }

                        return exampleCert;
                    };
                });
            });
        })
        .Build();

连接日志记录

调用 UseConnectionLogging 以发出用于进行连接上的字节级别通信的调试级别日志。 连接日志记录有助于排查低级通信中的问题,例如在 TLS 加密期间和代理后。 如果 UseConnectionLogging 放置在 UseHttps 之前,则会记录加密的流量。 如果 UseConnectionLogging 放置于 UseHttps 之后,则会记录解密的流量。

webBuilder.ConfigureKestrel(serverOptions =>
{
    serverOptions.Listen(IPAddress.Any, 8000, listenOptions =>
    {
        listenOptions.UseConnectionLogging();
    });
});

绑定到 TCP 套接字

Listen 方法绑定至 TCP 套接字,且 options lambda 允许 X.509 证书配置:

public static void Main(string[] args)
{
    CreateWebHostBuilder(args).Build().Run();
}

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .UseKestrel(serverOptions =>
        {
            serverOptions.Listen(IPAddress.Loopback, 5000);
            serverOptions.Listen(IPAddress.Loopback, 5001, listenOptions =>
            {
                listenOptions.UseHttps("testCert.pfx", "testPassword");
            });
        });
public static void Main(string[] args)
{
    CreateWebHostBuilder(args).Build().Run();
}

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .UseKestrel(serverOptions =>
        {
            serverOptions.Listen(IPAddress.Loopback, 5000);
            serverOptions.Listen(IPAddress.Loopback, 5001, listenOptions =>
            {
                listenOptions.UseHttps("testCert.pfx", "testPassword");
            });
        });

示例使用 ListenOptions 为终结点配置 HTTPS。 可使用相同 API 为特定终结点配置其他 Kestrel 设置。

在 Windows 上,可以使用 New-SelfSignedCertificate PowerShell cmdlet 创建自签名证书。 有关不支持的示例,请参阅 UpdateIISExpressSSLForChrome.ps1

在 macOS、Linux 和 Windows 上,可以使用 OpenSSL 创建证书。

绑定到 Unix 套接字

可通过 ListenUnixSocket 侦听 Unix 套接字以提高 Nginx 的性能,如以下示例所示:

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .UseKestrel(serverOptions =>
        {
            serverOptions.ListenUnixSocket("/tmp/kestrel-test.sock");
            serverOptions.ListenUnixSocket("/tmp/kestrel-test.sock", listenOptions =>
            {
                listenOptions.UseHttps("testCert.pfx", "testpassword");
            });
        });
  • 在 Nginx confiuguration 文件中,将 server > location > proxy_pass 条目设置为 http://unix:/tmp/{KESTREL SOCKET}:/; {KESTREL SOCKET} 是提供给 ListenUnixSocket 的套接字的名称(例如,上述示例中的 kestrel-test.sock)。
  • 确保套接字可由 Nginx (例如 chmod go+w /tmp/kestrel-test.sock)进行写入。

端口 0

如果指定端口号 0,Kestrel 将动态绑定到可用端口。 以下示例演示如何确定 Kestrel 在运行时实际绑定到的端口:

public void Configure(IApplicationBuilder app)
{
    var serverAddressesFeature = 
        app.ServerFeatures.Get<IServerAddressesFeature>();

    app.UseStaticFiles();

    app.Run(async (context) =>
    {
        context.Response.ContentType = "text/html";
        await context.Response
            .WriteAsync("<!DOCTYPE html><html lang=\"en\"><head>" +
                "<title></title></head><body><p>Hosted by Kestrel</p>");

        if (serverAddressesFeature != null)
        {
            await context.Response
                .WriteAsync("<p>Listening on the following addresses: " +
                    string.Join(", ", serverAddressesFeature.Addresses) +
                    "</p>");
        }

        await context.Response.WriteAsync("<p>Request URL: " +
            $"{context.Request.GetDisplayUrl()}<p>");
    });
}

在应用运行时,控制台窗口输出指示可用于访问应用的动态端口:

Listening on the following addresses: http://127.0.0.1:48508

限制

使用以下方法配置终结点:

  • UseUrls
  • --urls 命令行参数
  • urls 主机配置键
  • ASPNETCORE_URLS 环境变量

若要将代码用于 Kestrel 以外的服务器,这些方法非常有用。 不过,请注意以下限制:

  • HTTPS 无法与这些方法结合使用,除非在 HTTPS 终结点配置中提供了默认证书(例如,使用 KestrelServerOptions 配置或配置文件,如本主题前面的部分所示)。
  • 如果同时使用 ListenUseUrls 方法,Listen 终结点将覆盖 UseUrls 终结点。

IIS 终结点配置

使用 IIS 时,由 ListenUseUrls 设置用于 IIS 覆盖绑定的 URL 绑定。 有关详细信息,请参阅 ASP.NET Core 模块主题。

传输配置

对于 ASP.NET Core 2.1 版,Kestrel 默认传输不再基于 Libuv,而是基于托管的套接字。 这是 ASP.NET Core 2.0 应用升级到 2.1 时的一个重大更改,它调用 UseLibuv 并依赖于以下包中的一个:

对于需要使用 Libuv 的项目:

  • 将用于 Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv 包的依赖项添加到应用的项目文件:

    <PackageReference Include="Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv"
                      Version="{VERSION}" />
    
  • 调用 UseLibuv

    public class Program
    {
        public static void Main(string[] args)
        {
            CreateWebHostBuilder(args).Build().Run();
        }
    
        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseLibuv()
                .UseStartup<Startup>();
    }
    

URL 前缀

如果使用 UseUrls--urls 命令行参数、urls 主机配置键或 ASPNETCORE_URLS 环境变量,URL 前缀可采用以下任意格式。

仅 HTTP URL 前缀是有效的。 使用 UseUrls 配置 URL 绑定时,Kestrel 不支持 HTTPS。

  • 包含端口号的 IPv4 地址

    http://65.55.39.10:80/
    

    0.0.0.0 是一种绑定到所有 IPv4 地址的特殊情况。

  • 包含端口号的 IPv6 地址

    http://[0:0:0:0:0:ffff:4137:270a]:80/
    

    [::] 是 IPv4 0.0.0.0 的 IPv6 等效项。

  • 包含端口号的主机名

    http://contoso.com:80/
    http://*:80/
    

    主机名、*+ 并不特殊。 没有识别为有效 IP 地址或 localhost 的任何内容都将绑定到所有 IPv4 和 IPv6 IP。 若要将不同主机名绑定到相同端口上的不同 ASP.NET Core 应用,请使用 HTTP.sys 或 IIS、Nginx 或 Apache 等反向代理服务器。

    警告

    采用反向代理配置进行托管需要主机筛选

  • 包含端口号的主机 localhost 名称或包含端口号的环回 IP

    http://localhost:5000/
    http://127.0.0.1:5000/
    http://[::1]:5000/
    

    指定 localhost 后,Kestrel 将尝试绑定到 IPv4 和 IPv6 环回接口。 如果其他服务正在任一环回接口上使用请求的端口,则 Kestrel 将无法启动。 如果任一环回接口出于任何其他原因(通常是因为 IPv6 不受支持)而不可用,则 Kestrel 将记录一个警告。

主机筛选

尽管 Kestrel 支持基于前缀的配置(例如 http://example.com:5000),但 Kestrel 在很大程度上会忽略主机名。 主机 localhost 是一个特殊情况,用于绑定至环回地址。 除了显式 IP 地址以外的所有主机都绑定至所有公共 IP 地址。 不验证 Host 标头。

解决方法是,使用主机筛选中间件。 主机筛选中间件由 Microsoft.AspNetCore.HostFiltering 包提供,此包包含在 Microsoft.AspNetCore.App 元包中(ASP.NET Core 2.1 或 2.2)。 由调用 AddHostFilteringCreateDefaultBuilder 添加中间件:

public class Program
{
    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>();
}

默认情况下,主机筛选中间件处于禁用状态。 要启用该中间件,请在 appsettings.json/appsettings.<EnvironmentName>.json 中定义一个 AllowedHosts 键。 此值是以分号分隔的不带端口号的主机名列表:

appsettings.json:

{
  "AllowedHosts": "example.com;localhost"
}

备注

转接头中间件 同样包含 AllowedHosts 选项。 转接头中间件和主机筛选中间件具有适合不同方案的相似功能。 如果未保留 Host 标头,并且使用反向代理服务器或负载均衡器转接请求,则使用转接头中间件设置 AllowedHosts 比较合适。 将 Kestrel 用作面向公众的边缘服务器或直接转接 Host 标头时,使用主机筛选中间件设置 AllowedHosts 比较合适。

有关转接头中间件的详细信息,请参阅 配置 ASP.NET Core 以使用代理服务器和负载均衡器

其他资源

上一篇:托管和部署 ASP.NET Core

下一篇:ASP.NET Core 中的 HTTP.sys Web 服务器实现

关注微信小程序
程序员编程王-随时随地学编程

扫描二维码
程序员编程王

扫一扫关注最新编程教程