ASP.NET Core 中的响应压缩

网络带宽是一种有限资源。 减小响应大小通常可显著提高应用的响应速度。 减少有效负载大小的一种方法是压缩来自应用程序的响应。 本文介绍如何在 ASP.NET Core 中使用响应压缩中间件为应用实现响应压缩。

使用 HTTPS 探索压缩

可以通过 EnableForHttps 选项控制安全连接上的压缩响应,该选项由于安全风险,默认处于禁用状态。 使用压缩功能的动态生成页面可能使应用程序暴露于 CRIMEBREACH 攻击。 CRIME 和 BREACH 攻击可以通过防伪造令牌在 ASP.NET Core 中得到缓解。 有关详细信息,请参阅在 ASP.NET Core 中预防跨网站请求伪造 (XSRF/CSRF) 攻击。 有关缓解BREACH攻击的信息,请参阅http://www.breachattack.com/缓解措施

即使应用禁用 EnableForHttps 属性(false)、Internet Information Services(IIS)、IIS Express 和 Azure 应用服务也可以在 IIS Web 服务器上应用 Gzip。 查看响应标头时,请记下 服务器 标头值。 意外的 content-encoding 响应标头值可能是 Web 服务器的结果,而不是 ASP.NET Core应用配置的结果。

确定何时使用响应压缩中间件

在 IIS、Apache 或 Nginx 中使用基于服务器的响应压缩技术。 响应压缩中间件的性能可能与服务器模块的性能不匹配。 HTTP.sys 服务器和Kestrel服务器当前不提供内置压缩支持。

在以下情况下使用响应压缩中间件:

探索响应压缩

通常,任何未本机压缩的响应都可以从响应压缩中获益。 未本机压缩的响应通常包括:CSS、JavaScript、HTML、XML 和 JSON。 不要压缩本机压缩的资产,例如 PNG 文件。 尝试进一步压缩原生已压缩的响应时,处理压缩所需的时间可能会超过在大小和传输时间上任何额外的小幅减少。 不要压缩小于 150 - 1,000 字节的文件,具体取决于文件的内容和压缩效率。 压缩小文件的开销可能会导致生成的压缩文件比未压缩的文件更大。

当客户端可以处理压缩的内容时,客户端必须使用请求发送 Accept-Encoding 标头来通知服务器其功能。 当服务器发送压缩内容时,它必须包含 内容编码 标头中有关压缩响应编码方式的信息。

下表显示了 Accept-Encoding 响应头的内容编码指定,并指示响应压缩中间件是否支持该指定。

职务 Middleware Format 详细信息
br 是(默认值) Brotli 压缩数据格式 RFC 7932
deflate No DEFLATE 压缩数据格式 RFC 1951
exi No 高效 XML 交换 (EXI) W3C 建议
gzip Yes Gzip 文件格式 RFC 1952
identity Yes “无编码” - 不能对响应进行编码 排查响应压缩问题
pack200-gzip No Java存档的网络传输格式 JSR 200
*(星号) Yes “通配符”——未明确请求的任何可用内容编码 排查响应压缩问题

有关详细信息,请参阅 HTTP 参数的 IANA 官方内容编码列表

响应压缩中间件允许为自定义 Accept-Encoding 标头值添加其他压缩提供程序。 有关详细信息,请参阅本文后面的 自定义提供程序

由客户端发送时,响应压缩中间件能够对质量值 (qvalue, q) 权重做出反应,以设置压缩方案的优先级。 有关详细信息,请参阅 RFC 9110:HTTP 语义(第 12.5.3 节 Accept-Encoding)。

压缩算法需要在压缩速度和压缩效率之间进行权衡。 在此上下文中,有效性是指压缩后的输出大小。 最小大小通过最佳压缩实现。

下表介绍了请求、发送、缓存和接收压缩内容所涉及的标头。

Header Role 详细信息
Accept-Encoding 从客户端发送到服务器以指示客户端可接受的内容编码方案。 Accept-Encoding 标头
Content-Encoding 从服务器发送到客户端以指示有效负载中内容的编码。 Content-Encoding 标头
Content-Length 当压缩发生时,Content-Length 标头会被删除,因为压缩响应时正文内容会发生更改。 Content-Length 标头
Content-MD5 压缩发生时, Content-MD5 会删除标头,因为正文内容已更改,哈希不再有效。 RFC 1864:Content-MD5 标头字段
Content-Type 指定内容的 MIME 类型。 每个响应都应指定其 Content-Type 值。 响应压缩中间件会检查此值,以确定是否应压缩响应。 响应压缩中间件指定一组可以编码 的默认 MIME 类型 ,并且可以替换或添加它们。 Content-Type 标头
Vary 当服务器将值为 Accept-EncodingVary 标头发送到客户端和代理时,该标头会指示客户端或代理应该根据请求的 Accept-Encoding 标头值缓存(改变)响应。 带有 Vary: Accept-Encoding 标头的返回内容的结果是,压缩的响应和未压缩的响应都单独进行缓存。 改变标头

使用示例应用了解响应压缩中间件的功能。 该示例说明:

  • 使用 Gzip 和自定义压缩提供程序来压缩应用程序的响应。
  • 如何将 MIME 类型添加到 MIME 类型的默认列表中进行压缩。
  • 如何添加自定义响应压缩提供程序。

配置响应压缩中间件

以下代码演示如何为默认 MIME 类型和压缩提供程序(BrotliGzip)启用响应压缩中间件:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddResponseCompression(options =>
{
    options.EnableForHttps = true;
});

var app = builder.Build();

app.UseResponseCompression();

app.MapGet("/", () => "Hello World!");

app.Run();

有关响应压缩中间件的说明

使用响应压缩中间件时,请记住以下几点:

向示例应用提交不带 Accept-Encoding 标头的请求,并观察响应是否未压缩。 标头 Content-Encoding 不在响应标头集合中。

例如,在 Firefox 开发者版本中:

  1. 选择“网络”选项卡。
  2. 右键单击 “网络请求”列表中的 请求,然后选择“ 编辑并重新发送”。
  3. Accept-Encoding: 的值从 gzip, deflate, br 更改为 none
  4. 选择“ 发送”。

使用浏览器的开发人员工具向示例应用提交请求,并确认响应已被压缩。 响应中存在 Content-EncodingVary 标头。

审核服务商

本部分提供有关压缩提供程序的详细信息,包括 Brotli、Gzip 和自定义提供程序。

Brotli 和 Gzip 压缩提供程序

使用 BrotliCompressionProvider 类来通过 RFC 7932:Brotli 压缩数据格式压缩响应。

如果未显式添加压缩提供程序CompressionProviderCollection类:

  • 默认情况下,Brotli 和 Gzip 压缩提供程序将添加到压缩提供程序数组。
  • 当客户端支持 Brotli 压缩数据格式时,压缩默认为 Brotli 压缩。
  • 如果客户端不支持 Brotli,则当客户端支持 Gzip 压缩时,压缩默认为 Gzip。

添加一个压缩提供程序时,不会添加其他提供程序。 例如,如果 Gzip 压缩提供程序是显式添加的唯一提供程序,则不会添加其他压缩提供程序。

Note

指向 .NET 参考源的文档链接通常会加载存储库的默认分支,该分支表示针对下一个 .NET 版本的当前开发。 若要为特定版本选择标签,请使用“切换分支或标签”下拉列表。 有关详细信息,请参阅如何选择 ASP.NET Core 源代码的版本标记 (dotnet/AspNetCore.Docs #26205)

下面的代码:

  • 为 HTTPS 请求启用响应压缩。
  • 添加 Brotli 和 Gzip 响应压缩提供程序。
using System.IO.Compression;
using Microsoft.AspNetCore.ResponseCompression;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddResponseCompression(options =>
{
    options.EnableForHttps = true;
    options.Providers.Add<BrotliCompressionProvider>();
    options.Providers.Add<GzipCompressionProvider>();
});

builder.Services.Configure<BrotliCompressionProviderOptions>(options =>
{
    options.Level = CompressionLevel.Fastest;
});

builder.Services.Configure<GzipCompressionProviderOptions>(options =>
{
    options.Level = CompressionLevel.SmallestSize;
});

var app = builder.Build();

app.UseResponseCompression();

app.MapGet("/", () => "Hello World!");

app.Run();

使用 BrotliCompressionProviderOptions 类和 GzipCompressionProviderOptions 类设置压缩级别。 Brotli 和 Gzip 压缩提供程序默认为由 CompressionLevel.Fastest 枚举设定的最快压缩级别。 但是,此方法可能不会产生最有效的压缩。 如果需要最高效的压缩,请配置响应压缩中间件以实现最佳压缩。

有关表示压缩操作是强调速度还是压缩率的值,请参阅 CompressionLevel 枚举

using System.IO.Compression;
using Microsoft.AspNetCore.ResponseCompression;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddResponseCompression(options =>
{
    options.EnableForHttps = true;
    options.Providers.Add<BrotliCompressionProvider>();
    options.Providers.Add<GzipCompressionProvider>();
});

builder.Services.Configure<BrotliCompressionProviderOptions>(options =>
{
    options.Level = CompressionLevel.Fastest;
});

builder.Services.Configure<GzipCompressionProviderOptions>(options =>
{
    options.Level = CompressionLevel.SmallestSize;
});

var app = builder.Build();

app.UseResponseCompression();

app.MapGet("/", () => "Hello World!");

app.Run();

自定义提供程序

使用 ICompressionProvider 接口创建自定义压缩实现。 该 EncodingName 属性表示此 ICompressionProvider 生成的内容编码。 响应压缩中间件使用这些信息根据请求的 Accept-Encoding 标头中指定的列表来选择提供程序。

对具有 Accept-Encoding: mycustomcompression 标头的示例应用的请求将返回具有 Content-Encoding: mycustomcompression 标头的响应。 客户端必须能够解压缩自定义编码,以便自定义压缩实现正常工作。

using Microsoft.AspNetCore.ResponseCompression;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddResponseCompression(options =>
{
    options.Providers.Add<BrotliCompressionProvider>();
    options.Providers.Add<GzipCompressionProvider>();
    options.Providers.Add<CustomCompressionProvider>();
});

var app = builder.Build();

app.UseResponseCompression();

app.MapGet("/", () => "Hello World!");

app.Run();
using Microsoft.AspNetCore.ResponseCompression;

public class CustomCompressionProvider : ICompressionProvider
{
    public string EncodingName => "mycustomcompression";
    public bool SupportsFlush => true;

    public Stream CreateStream(Stream outputStream)
    {
        // Replace with a custom compression stream wrapper.
        return outputStream;
    }
}

在前面的代码中,示例不会压缩响应正文。 但是,该示例演示了在何处实现自定义压缩算法。

查看 MIME 类型

响应压缩中间件为压缩指定一组默认的 MIME 类型。 查看 源代码,了解受支持的 MIME 类型的完整列表

Note

指向 .NET 参考源的文档链接通常会加载存储库的默认分支,该分支表示针对下一个 .NET 版本的当前开发。 若要为特定版本选择标签,请使用“切换分支或标签”下拉列表。 有关详细信息,请参阅如何选择 ASP.NET Core 源代码的版本标记 (dotnet/AspNetCore.Docs #26205)

将 MIME 类型替换为 ResponseCompressionOptions.MimeTypes 属性或追加 MIME 类型 。 不支持通配符 MIME 类型,例如 text/* 。 示例应用为 image/svg+xml 添加 MIME 类型,并压缩和提供 ASP.NET Core 横幅图像 banner.svg

using Microsoft.AspNetCore.ResponseCompression;
using ResponseCompressionSample;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddResponseCompression(options =>
{
    options.EnableForHttps = true;
    options.Providers.Add<BrotliCompressionProvider>();
    options.Providers.Add<GzipCompressionProvider>();
    options.Providers.Add<CustomCompressionProvider>();
    options.MimeTypes =
    ResponseCompressionDefaults.MimeTypes.Concat(
        new[] { "image/svg+xml" });
});

var app = builder.Build();

app.UseResponseCompression();

添加 Vary 标头

基于 Accept-Encoding 请求标头的压缩响应可以包括未压缩和不同版本的压缩响应。 为指示客户端和代理缓存存在且应存储多个版本,请添加 Vary 标头,并设置其值为 Accept-Encoding。 响应中间件会在压缩响应时自动在ResponseCompressionBody.cs文件中添加“Vary”标头

Note

指向 .NET 参考源的文档链接通常会加载存储库的默认分支,该分支表示针对下一个 .NET 版本的当前开发。 若要为特定版本选择标记,请使用“切换分支或标记”下拉列表。 有关详细信息,请参阅如何选择 ASP.NET Core 源代码的版本标记 (dotnet/AspNetCore.Docs #26205)

Nginx 反向代理的问题

当 Nginx 代理请求时,标头会被删除。 删除 Accept-Encoding 标头会阻止响应压缩中间件压缩响应。 有关详细信息,请参阅 Nginx:压缩和解压缩。 此问题在 GitHub dotnet/aspnetcore 问题 #5989 - 中跟踪 Nginx 的直通压缩方案的确定。

禁用 IIS 动态压缩

若要禁用在服务器级别配置的 IIS 动态压缩模块,请参阅 禁用 IIS 模块

排查响应压缩问题

使用 Firefox Browser - 开发人员版 等工具,可以设置 Accept-Encoding 请求标头并研究响应标头、大小和正文。 默认情况下,响应压缩中间件压缩满足以下条件的响应:

  • 标头Accept-Encoding包含与自定义压缩提供程序匹配的值brgzip*(星号)或自定义编码。 该值不得等于 identity(即无编码),也不得将质量值(qvalue,q)设置为 0(零)。

  • 必须设置 MIME 类型(Content-Type),并且必须与类上 ResponseCompressionOptions 配置的 MIME 类型匹配。

  • 请求不得包含 Content-Range 标头

  • 除非在响应压缩中间件选项中配置了安全超文本协议(https),否则请求必须使用不安全的超文本协议(https)。

    Important

    请查看与启用安全内容压缩相关的风险,如本文前面的 HTTPS 压缩 中所述。

查看已部署Azure示例

部署到Azure的示例应用具有以下 Program.cs 文件:

using Microsoft.AspNetCore.ResponseCompression;
using ResponseCompressionSample;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddResponseCompression(options =>
{
    options.EnableForHttps = true;
    options.Providers.Add<BrotliCompressionProvider>();
    options.Providers.Add<GzipCompressionProvider>();
    options.Providers.Add<CustomCompressionProvider>();
    options.MimeTypes =
    ResponseCompressionDefaults.MimeTypes.Concat(
        new[] { "image/svg+xml" });
});

var app = builder.Build();

app.UseResponseCompression();

app.Map("/trickle", async (HttpResponse httpResponse) =>
{
    httpResponse.ContentType = "text/plain;charset=utf-8";

    for (int i = 0; i < 20; i++)
    {
        await httpResponse.WriteAsync("a");
        await httpResponse.Body.FlushAsync();
        await Task.Delay(TimeSpan.FromMilliseconds(50));
    }
});

app.Map("/testfile1kb.txt", () => Results.File(
    app.Environment.ContentRootFileProvider.GetFileInfo("testfile1kb.txt").PhysicalPath,
    "text/plain;charset=utf-8"));

app.Map("/banner.svg", () => Results.File(
    app.Environment.ContentRootFileProvider.GetFileInfo("banner.svg").PhysicalPath,
    "image/svg+xml;charset=utf-8"));

app.MapFallback(() => LoremIpsum.Text);

app.Run();

网络带宽是一种有限资源。 减小响应大小通常可显著提高应用的响应速度。 减小有效负载大小的一种方式是压缩应用的响应。

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

何时使用响应压缩中间件

在 IIS、Apache 或 Nginx 中使用基于服务器的响应压缩技术。 中间件的性能可能与服务器模块的性能不匹配。 HTTP.sys 服务器和 Kestrel 服务器当前不提供内置压缩支持。

在以下情况下使用响应压缩中间件:

响应压缩

通常,任何未本机压缩的响应都可以从响应压缩中获益。 未本机压缩的响应通常包括:CSS、JavaScript、HTML、XML 和 JSON。 不应压缩已经自身压缩的资源,例如 PNG 文件。 如果尝试进一步压缩本机压缩的响应,则大小和传输时间的任何小幅额外减少都可能被处理压缩所花费的时间所掩盖。 不要压缩小于约 150-1000 字节的文件(具体取决于文件的内容和压缩效率)。 压缩小文件的开销可能会导致生成的压缩文件比原文件更大。

当客户端可以处理压缩内容时,客户端必须通过随请求发送 Accept-Encoding 标头来通知服务器其功能。 当服务器发送压缩的内容时,它必须在 Content-Encoding 标头中包含有关如何对压缩响应进行编码的信息。 下表显示了中间件支持的内容编码指定内容。

Accept-Encoding 标头值 中间件支持 Description
br 是(默认值) Brotli 压缩数据格式
deflate No DEFLATE 压缩数据格式
exi No W3C 高效 XML 交换
gzip Yes Gzip 文件格式
identity Yes “无编码”标识符:不得对响应进行编码。
pack200-gzip No Java 存档的网络传输格式
* Yes 任何没有明确请求的可用内容编码

有关详细信息,请参阅 IANA 官方内容编码列表

通过中间件,可为自定义 Accept-Encoding 标头值添加其他压缩提供程序。 请参阅下面的自定义提供程序,了解有关详细信息。

由客户端发送时,中间件能够对质量值 (qvalue, q) 权重做出反应,以设置压缩方案的优先级。 有关详细信息,请参阅 RFC 9110:Accept-Encoding

压缩算法需要在压缩速度和压缩效率之间进行权衡。 在此上下文中,有效性是指压缩后的输出大小。 最小尺寸通过最佳压缩实现

下表描述了请求、发送、缓存和接收压缩内容所涉及的标头。

Header Role
Accept-Encoding 从客户端发送到服务器以指示客户端可接受的内容编码方案。
Content-Encoding 从服务器发送到客户端以指示有效负载中内容的编码。
Content-Length 发生压缩时,会删除 Content-Length 标头,因为压缩响应时正文内容会发生更改。
Content-MD5 发生压缩时,会删除 Content-MD5 标头,因为正文内容已更改,哈希不再有效。
Content-Type 指定内容的 MIME 类型。 每个响应都应指定其 Content-Type。 中间件检查此值,确定是否应压缩响应。 中间件指定了一组可以编码的默认 MIME 类型,但你可以替换或添加 MIME 类型。
Vary 当服务器将值为 Accept-EncodingVary 标头发送到客户端和代理时,该标头会指示客户端或代理应该根据请求的 Accept-Encoding 标头值缓存(改变)响应。 带有 Vary: Accept-Encoding 标头的返回内容的结果是,压缩的响应和未压缩的响应都单独进行缓存。

使用示例应用了解响应压缩中间件的功能。 该示例说明:

  • 应用响应的压缩,使用 Gzip 和自定义压缩器。
  • 如何将 MIME 类型添加到 MIME 类型的默认列表中进行压缩。

Configuration

以下代码演示如何为默认 MIME 类型和压缩提供程序(BrotliGzip)启用响应压缩中间件:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddResponseCompression();
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        app.UseResponseCompression();
    }
}

Notes:

向示例应用提交不带 Accept-Encoding 标头的请求,并观察响应是否未压缩。 响应中不存在 Content-EncodingVary 标头。

Fiddler 窗口显示了没有接收编码标头的请求的结果。该响应未压缩。

向示例应用提交带 Accept-Encoding: br 标头(Brotli 压缩)的请求,并观察响应是否已压缩。 响应中存在 Content-EncodingVary 标头。

Fiddler 窗口显示了一个请求的结果,该请求包含 Accept-Encoding 标头,其值为 br。在响应中添加了 Vary 和 Content-Encoding 标头。该响应内容已被压缩。

Providers

Brotli 压缩提供程序

使用 BrotliCompressionProviderBrotli 压缩数据格式压缩响应。

如果未明确添加压缩提供程序到 CompressionProviderCollection:

  • 默认情况下,Brotli 压缩提供程序与 Gzip 压缩提供程序一起添加到压缩提供程序数组中。
  • 当客户端支持 Brotli 压缩数据格式时,压缩默认为 Brotli 压缩。 如果客户端不支持 Brotli,则当客户端支持 Gzip 压缩时,压缩默认为 Gzip。
public void ConfigureServices(IServiceCollection services)
{
    services.AddResponseCompression();
}

显式添加任何压缩提供程序时,必须添加 Brotli 压缩提供程序:

public void ConfigureServices(IServiceCollection services)
{
    services.AddResponseCompression(options =>
    {
        options.Providers.Add<BrotliCompressionProvider>();
        options.Providers.Add<GzipCompressionProvider>();
        options.Providers.Add<CustomCompressionProvider>();
        options.MimeTypes = 
            ResponseCompressionDefaults.MimeTypes.Concat(
                new[] { "image/svg+xml" });
    });
}

使用 BrotliCompressionProviderOptions 设置压缩级别。 Brotli 压缩提供程序默认为最快的压缩级别 (CompressionLevel.Fastest),这可能不会产生最有效的压缩。 如果需要最高效的压缩,请配置中间件以实现最佳压缩。

压缩级别 Description
CompressionLevel.Fastest 即使生成的输出未以最佳方式压缩,压缩也应尽快完成。
CompressionLevel.NoCompression 不应执行压缩。
CompressionLevel.Optimal 即使压缩需要更多时间才能完成,也应以最佳方式压缩响应。
public void ConfigureServices(IServiceCollection services)
{
    services.AddResponseCompression();

    services.Configure<BrotliCompressionProviderOptions>(options => 
    {
        options.Level = CompressionLevel.Fastest;
    });
}

Gzip 压缩提供程序

使用 GzipCompressionProviderGzip 文件格式压缩响应。

如果未明确添加压缩提供程序到 CompressionProviderCollection:

  • 默认情况下,Gzip 压缩提供程序与 Brotli 压缩提供程序一起添加到压缩提供程序数组中。
  • 当客户端支持 Brotli 压缩数据格式时,压缩默认为 Brotli 压缩。 如果客户端不支持 Brotli,则当客户端支持 Gzip 压缩时,压缩默认为 Gzip。
public void ConfigureServices(IServiceCollection services)
{
    services.AddResponseCompression();
}

当显式添加任何压缩提供程序时,必须添加 Gzip 压缩提供程序。

public void ConfigureServices(IServiceCollection services)
{
    services.AddResponseCompression(options =>
    {
        options.Providers.Add<BrotliCompressionProvider>();
        options.Providers.Add<GzipCompressionProvider>();
        options.Providers.Add<CustomCompressionProvider>();
        options.MimeTypes = 
            ResponseCompressionDefaults.MimeTypes.Concat(
                new[] { "image/svg+xml" });
    });
}

使用 GzipCompressionProviderOptions 设置压缩级别。 Gzip 压缩提供程序默认为最快的压缩级别 (CompressionLevel.Fastest),这可能不会产生最有效的压缩。 如果需要最高效的压缩,请配置中间件以实现最佳压缩。

压缩级别 Description
CompressionLevel.Fastest 即使生成的输出未以最佳方式压缩,压缩也应尽快完成。
CompressionLevel.NoCompression 不应执行压缩。
CompressionLevel.Optimal 即使压缩需要更多时间才能完成,也应以最佳方式压缩响应。
public void ConfigureServices(IServiceCollection services)
{
    services.AddResponseCompression();

    services.Configure<GzipCompressionProviderOptions>(options => 
    {
        options.Level = CompressionLevel.Fastest;
    });
}

自定义提供程序

使用 ICompressionProvider 创建自定义压缩实现。 EncodingName 表示此 ICompressionProvider 生成的内容编码。 中间件使用这些信息根据请求的 Accept-Encoding 标头中指定的列表来选择提供程序。

客户端使用示例应用提交带有 Accept-Encoding: mycustomcompression 标头的请求。 中间件使用自定义压缩实现,并返回带有 Content-Encoding: mycustomcompression 标头的响应。 客户端必须能够解压缩自定义编码,以便自定义压缩实现正常工作。

public void ConfigureServices(IServiceCollection services)
{
    services.AddResponseCompression(options =>
    {
        options.Providers.Add<BrotliCompressionProvider>();
        options.Providers.Add<GzipCompressionProvider>();
        options.Providers.Add<CustomCompressionProvider>();
        options.MimeTypes = 
            ResponseCompressionDefaults.MimeTypes.Concat(
                new[] { "image/svg+xml" });
    });
}
public class CustomCompressionProvider : ICompressionProvider
{
    public string EncodingName => "mycustomcompression";
    public bool SupportsFlush => true;

    public Stream CreateStream(Stream outputStream)
    {
        // Create a custom compression stream wrapper here
        return outputStream;
    }
}

向示例应用提交带 Accept-Encoding: mycustomcompression 标头的请求,并观察响应头。 响应中存在 VaryContent-Encoding 标头。 样本未压缩响应正文(正文未显示)。 示例的 CustomCompressionProvider 类中没有压缩实现。 但是,该示例演示了在何处实现此类压缩算法。

Fiddler 窗口显示了包含 Accept-Encoding 头和 mycustomcompression 值的请求的结果。Vary 和 Content-Encoding 头被添加到响应中。

MIME 类型

中间件为压缩指定一组默认的 MIME 类型:

  • application/javascript
  • application/json
  • application/xml
  • text/css
  • text/html
  • text/json
  • text/plain
  • text/xml

使用“响应压缩中间件”选项替换或追加 MIME 类型。 请注意,不支持通配符 MIME 类型,如 text/*。 示例应用为 image/svg+xml 添加 MIME 类型,并压缩和提供 ASP.NET Core 横幅图像 (banner.svg)。

public void ConfigureServices(IServiceCollection services)
{
    services.AddResponseCompression(options =>
    {
        options.Providers.Add<BrotliCompressionProvider>();
        options.Providers.Add<GzipCompressionProvider>();
        options.Providers.Add<CustomCompressionProvider>();
        options.MimeTypes = 
            ResponseCompressionDefaults.MimeTypes.Concat(
                new[] { "image/svg+xml" });
    });
}

使用安全协议进行压缩

可以通过 EnableForHttps 选项控制安全连接上的压缩响应,该选项默认处于禁用状态。 对动态生成的页面使用压缩可能会导致安全问题,例如 CRIMEBREACH 攻击。

添加 Vary 标头

在基于 Accept-Encoding 标头压缩响应时,可能有多个压缩版本的响应和一个未压缩版本。 为了告知客户端和代理缓存存在多个版本并且应存储这些版本,向 Vary 标头添加了 Accept-Encoding 值。 在 ASP.NET Core 2.0 或更高版本中,中间件在压缩响应时自动添加了 Vary 标头。

在 Nginx 反向代理后面时出现的中间件问题

当 Nginx 代理请求时,将删除 Accept-Encoding 头部字段。 删除 Accept-Encoding 标头会阻止中间件压缩响应。 有关详细信息,请参阅 NGINX:压缩和解压缩。 此问题由找出 Nginx 的直通压缩 (dotnet/aspnetcore#5989) 跟踪。

使用 IIS 动态压缩

如果在服务器级别配置了一个活动的 IIS 动态压缩模块,并希望为应用禁用该模块,请通过在 web.config 文件中添加配置来禁用该模块。 有关详细信息,请参阅禁用 IIS 模块

Troubleshooting

使用 FiddlerFirefox Browser Developer 等工具,可借助这些工具设置 Accept-Encoding 请求头,并研究响应头、大小和正文。 默认情况下,响应压缩中间件压缩满足以下条件的响应:

  • Accept-Encoding 标头存在,且值为 brgzip* 或与已建立的自定义压缩提供程序匹配的自定义编码。 该值不得为 identity,或将质量值 (qvalue, q) 设置为 0(零)。
  • 必须设置 MIME 类型 (Content-Type),并且该类型必须与在 ResponseCompressionOptions 上配置的 MIME 类型匹配。
  • 请求不得包含 Content-Range 标头。
  • 请求必须使用不安全协议 (http),除非在“响应压缩中间件”选项中配置了安全协议 (https)。 启用安全内容压缩时,请注意上述危险

其他资源