在ASP.NET Core MVC的操作级别压缩过滤器

时间:2018-06-18 23:28:01

标签: asp.net asp.net-mvc asp.net-core

我正在将ASP.NET MVC 5应用程序迁移到ASP.NET Core 2.1。在" old"应用程序我有一个压缩过滤器,它将Gzip应用于特定请求的响应。这样我就能够仅压缩特定请求而不是所有请求。

根据我的理解,ASP.NET Core使用中间件有不同的方法,它只是让你有机会对所有请求(将其添加到管道)应用压缩或根本不应用它。

有没有办法通过创建ActionFilterAttribute来实现我想要的?这是我的旧代码:

public class CompressAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            var acceptEncoding = filterContext.HttpContext.Request.Headers["Accept-Encoding"];

            if (string.IsNullOrEmpty(acceptEncoding))
                return;

            acceptEncoding = acceptEncoding.ToLower();
            var response = filterContext.HttpContext.Response;
            if (acceptEncoding.Contains("gzip"))
            {
                response.AppendHeader("Content-Encoding", "gzip");
                response.Filter = new GZipStream(response.Filter, CompressionMode.Compress);
            }
            else if (acceptEncoding.Contains("deflate"))
            {
                response.AppendHeader("Content-Encoding", "deflate");
                response.Filter = new DeflateStream(response.Filter, CompressionMode.Compress);
            }
        }
    }

2 个答案:

答案 0 :(得分:1)

我发现了两种将压缩应用于特定情况的方法:

1)创建中间件并在应用压缩之前分析请求。我不喜欢这种方法,因为中间件将针对所有请求运行,并且必须检查请求信息,从而影响性能。

2)修改Web.config文件(这是我采用的方法),并将压缩应用于某些动态和静态类型。发布ASP.NET Core应用后,会自动生成一个web.config。我们可以像下面的示例一样编辑web.config文件并添加压缩:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <urlCompression doStaticCompression="true" doDynamicCompression="true" />
    <httpCompression>
      <dynamicTypes>
        <clear />
        <add enabled="true" mimeType="text/*" />
        <add enabled="true" mimeType="message/*" />
        <add enabled="true" mimeType="application/x-javascript" />
        <add enabled="true" mimeType="application/javascript" />
        <add enabled="true" mimeType="application/json" />
        <add enabled="false" mimeType="*/*" />
        <add enabled="true" mimeType="application/atom+xml" />
        <add enabled="true" mimeType="application/atom+xml;charset=utf-8" />
      </dynamicTypes>
      <staticTypes>
        <clear />
        <add enabled="true" mimeType="text/*" />
        <add enabled="true" mimeType="message/*" />
        <add enabled="true" mimeType="application/javascript" />
        <add enabled="true" mimeType="application/atom+xml" />
        <add enabled="true" mimeType="application/xaml+xml" />
        <add enabled="false" mimeType="*/*" />
      </staticTypes>
    </httpCompression>
    <handlers>
      <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
    </handlers>
    <aspNetCore processPath="dotnet" arguments=".\YourApplicationName.dll" stdoutLogEnabled="false" stdoutLogFile="\\?\%home%\LogFiles\stdout" />
  </system.webServer>
</configuration>

答案 1 :(得分:0)

我还有一个任务是仅压缩某些特定操作/控制器的响应 最后,我找到了基于ActionFilterAttribute的解决方案。这是一个例子

namespace CommonWebApi.MiddleWare
{
    /// <summary>
    /// Attribute for enabling Brotli/GZip/Deflate compression for specied action
    /// </summary>
    public class ResponseCompressionAttribute : ActionFilterAttribute
    {
        private Stream _originStream = null;
        private MemoryStream _ms = null;

        public override void OnActionExecuting(ActionExecutingContext context)
        {
            HttpRequest request = context.HttpContext.Request;
            string acceptEncoding = request.Headers["Accept-Encoding"];
            if (string.IsNullOrEmpty(acceptEncoding)) return;
            acceptEncoding = acceptEncoding.ToUpperInvariant();
            HttpResponse response = context.HttpContext.Response;
            if (acceptEncoding.Contains("BR", StringComparison.OrdinalIgnoreCase))//Brotli 
            {
                if (!(response.Body is BrotliStream))// avoid twice compression.
                {
                    _originStream = response.Body;
                    _ms = new MemoryStream();
                    response.Headers.Add("Content-encoding", "br");
                    response.Body = new BrotliStream(_ms, CompressionLevel.Optimal);
                }
            }
            else if (acceptEncoding.Contains("GZIP", StringComparison.OrdinalIgnoreCase))
            {
                if (!(response.Body is GZipStream))
                {
                    _originStream = response.Body;
                    _ms = new MemoryStream();
                    response.Headers.Add("Content-Encoding", "gzip");
                    response.Body = new GZipStream(_ms, CompressionLevel.Optimal);
                }
            }
            else if (acceptEncoding.Contains("DEFLATE", StringComparison.OrdinalIgnoreCase))
            {
                if (!(response.Body is DeflateStream))
                {
                    _originStream = response.Body;
                    _ms = new MemoryStream();
                    response.Headers.Add("Content-encoding", "deflate");
                    response.Body = new DeflateStream(_ms, CompressionLevel.Optimal);
                }
            }
            base.OnActionExecuting(context);
        }

        public override async void OnResultExecuted(ResultExecutedContext context)
        {
            if ((_originStream != null) && (_ms != null))
            {
                HttpResponse response = context.HttpContext.Response;
                await response.Body.FlushAsync();
                _ms.Seek(0, SeekOrigin.Begin);
                response.Headers.ContentLength = _ms.Length;
                await _ms.CopyToAsync(_originStream);
                response.Body.Dispose();
                _ms.Dispose();
                response.Body = _originStream;
            }
            base.OnResultExecuted(context);
        }
    }
}

已知限制/要点:

  • 仅需要MemoryStream才能获得压缩流的长度。为了正确工作,应将该值设置为response.Headers.ContentLength
  • 当前它仅适用于3种压缩类型。