简体   繁体   English

返回HttpResponseMessage时的WebAPI Gzip

[英]WebAPI Gzip when returning HttpResponseMessage

I have a WebAPI controller that returns an HttpResponseMessage and I want to add gzip compression. 我有一个WebAPI控制器,它返回一个HttpResponseMessage ,我想添加gzip压缩。 This is the server code: 这是服务器代码:

using System.Net.Http;
using System.Web.Http;
using System.Web;
using System.IO.Compression;

[Route("SomeRoute")]
public HttpResponseMessage Post([FromBody] string value)
{
    HttpContext context = HttpContext.Current;

    context.Response.Filter = new GZipStream(context.Response.Filter, CompressionMode.Compress);

    HttpContext.Current.Response.AppendHeader("Content-encoding", "gzip");
    HttpContext.Current.Response.Cache.VaryByHeaders["Accept-encoding"] = true;

    return new SomeClass().SomeRequest(value);
}

And this is the client code for the ajax call, using jquery: 这是使用jquery的ajax调用的客户端代码:

$.ajax({
    url: "/SomeRoute",
    type: "POST",
    cache: "false",
    data: SomeData,
    beforeSend: function (jqXHR) { jqXHR.setRequestHeader('Accept-Encoding', 'gzip'); },
    success: function(msg) { ... }

When I run this, the server code returns without bugging but the client bugs: 当我运行它时,服务器代码返回没有错误,但客户端错误:

(failed)
net::ERR_CONTENT_DECODING_FAILED

在此输入图像描述

When I look with Fiddler, this is what I see: 当我和Fiddler一起看时,这就是我所看到的:

在此输入图像描述

What do I need to change to make the web service return gzipped content that the client processes normally? 我需要更改什么才能使Web服务返回客户端正常处理的gzip压缩内容? I know I could also do this with an HttpModule or through some setting on IIS but neither option fits the scenario of the hosting: 我知道我也可以使用HttpModule或通过IIS上的某些设置来执行此操作,但这两个选项都不适合托管的情况:

在此输入图像描述

Please note that I'm not looking for an IIS setting because I don't have access to that (hosting). 请注意,我不是在寻找IIS设置,因为我无权访问(托管)。

Add these NuGet packages: 添加这些NuGet包:

Microsoft.AspNet.WebApi.Extensions.Compression.Server System.Net.Http.Extensions.Compression.Client Microsoft.AspNet.WebApi.Extensions.Compression.Server System.Net.Http.Extensions.Compression.Client

Then and add one line of code to App_Start\\WebApiConfig.cs : 然后向App_Start\\WebApiConfig.cs添加一行代码:

GlobalConfiguration.Configuration.MessageHandlers.Insert(0, new ServerCompressionHandler(new GZipCompressor(), new DeflateCompressor()));

That will do the trick! 那就行了!

Details at: 详细信息:

Hope that helps. 希望有所帮助。

**Updated after comment from @JCisar ** @JCisar发表评论后更新

Update for ASP.Net Core ASP.Net Core的更新

Nuget Package is Nuget包是

Microsoft.AspNetCore.ResponseCompression Microsoft.AspNetCore.ResponseCompression

If you have access to IIS configuration 如果您有权访问IIS配置

You cant just apply the header and hope it will be gzipped - the response will not be zipped. 你不能只应用标题,并希望它将被gzip压缩 - 响应将不会被压缩。

You need remove the header you added and ensure you have the dynamic compression and static content compression are enabled on your IIS server. 您需要删除添加的标头,并确保在IIS服务器上启用了动态压缩和静态内容压缩。

One of the commenter's mentioned a good resource link here at stakoverflow that show how to do that: 其中一位评论者在stakoverflow上提到了一个很好的资源链接,展示了如何做到这一点:

Enable IIS7 gzip 启用IIS7 gzip

Note it will only work setting the value in web.config if dynamic compression is already installed (which is not in a default install of IIS) 请注意,如果已安装动态压缩(不在IIS的默认安装中),它将仅在web.config中设置值

You can find the information about this on MSDN documentation: http://www.iis.net/configreference/system.webserver/httpcompression 您可以在MSDN文档中找到有关此内容的信息: http//www.iis.net/configreference/system.webserver/httpcompression

Simple compression 简单的压缩

Below is using a simple example of doing your own compression this example is using the Web Api MVC 4 project from visual studio project templates. 下面是使用一个简单的自己进行压缩的例子,这个例子是使用visual studio项目模板中的Web Api MVC 4项目。 To get compression working for HttpResponseMessages you have to implement a custom MessageHandler. 要使压缩工作HttpResponseMessages,您必须实现自定义MessageHandler。 See below a working example. 请参见下面的工作示例。

See the code implementation below. 请参阅下面的代码实现。

Please note that I tried to keep the method doing the same as your example. 请注意,我试图让方法与您的示例保持一致。

using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;

namespace MvcApplication1.Controllers
{
    public class ValuesController : ApiController
    {
        public class Person
        {
            public string name { get; set; }
        }
        // GET api/values
        public IEnumerable<string> Get()
        {
            HttpContext.Current.Response.Cache.VaryByHeaders["accept-encoding"] = true;

            return new [] { "value1", "value2" };
        }

        // GET api/values/5
        public HttpResponseMessage Get(int id)
        {
            HttpContext.Current.Response.Cache.VaryByHeaders["accept-encoding"] = true;

            var TheHTTPResponse = new HttpResponseMessage(System.Net.HttpStatusCode.OK); 
            TheHTTPResponse.Content = new StringContent("{\"asdasdasdsadsad\": 123123123 }", Encoding.UTF8, "text/json"); 

            return TheHTTPResponse;
        }

        public class EncodingDelegateHandler : DelegatingHandler
        {
            protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
            {
                return base.SendAsync(request, cancellationToken).ContinueWith<HttpResponseMessage>((responseToCompleteTask) =>
                {
                    HttpResponseMessage response = responseToCompleteTask.Result;

                    if (response.RequestMessage.Headers.AcceptEncoding != null &&
                        response.RequestMessage.Headers.AcceptEncoding.Count > 0)
                    {
                        string encodingType = response.RequestMessage.Headers.AcceptEncoding.First().Value;

                        response.Content = new CompressedContent(response.Content, encodingType);
                    }

                    return response;
                },
                TaskContinuationOptions.OnlyOnRanToCompletion);
            }
        }

        public class CompressedContent : HttpContent
        {
            private HttpContent originalContent;
            private string encodingType;

            public CompressedContent(HttpContent content, string encodingType)
            {
                if (content == null)
                {
                    throw new ArgumentNullException("content");
                }

                if (encodingType == null)
                {
                    throw new ArgumentNullException("encodingType");
                }

                originalContent = content;
                this.encodingType = encodingType.ToLowerInvariant();

                if (this.encodingType != "gzip" && this.encodingType != "deflate")
                {
                    throw new InvalidOperationException(string.Format("Encoding '{0}' is not supported. Only supports gzip or deflate encoding.", this.encodingType));
                }

                // copy the headers from the original content
                foreach (KeyValuePair<string, IEnumerable<string>> header in originalContent.Headers)
                {
                    this.Headers.TryAddWithoutValidation(header.Key, header.Value);
                }

                this.Headers.ContentEncoding.Add(encodingType);
            }

            protected override bool TryComputeLength(out long length)
            {
                length = -1;

                return false;
            }

            protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
            {
                Stream compressedStream = null;

                if (encodingType == "gzip")
                {
                    compressedStream = new GZipStream(stream, CompressionMode.Compress, leaveOpen: true);
                }
                else if (encodingType == "deflate")
                {
                    compressedStream = new DeflateStream(stream, CompressionMode.Compress, leaveOpen: true);
                }

                return originalContent.CopyToAsync(compressedStream).ContinueWith(tsk =>
                {
                    if (compressedStream != null)
                    {
                        compressedStream.Dispose();
                    }
                });
            }
        }
    }
}

Also add the new message handler to the config of your app. 还要将新消息处理程序添加到应用程序的配置中。

using System.Web.Http;
using MvcApplication1.Controllers;

namespace MvcApplication1
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

            config.MessageHandlers.Add(new ValuesController.EncodingDelegateHandler());

            config.EnableSystemDiagnosticsTracing();
        }
    }
}

The Custom handler was put together by - Kiran Challa ( http://blogs.msdn.com/b/kiranchalla/archive/2012/09/04/handling-compression-accept-encoding-sample.aspx ) 自定义处理程序由 - Kiran Challa( http://blogs.msdn.com/b/kiranchalla/archive/2012/09/04/handling-compression-accept-encoding-sample.aspx )组合在一起。

There are better examples that implement deflating of inbound streams too you can see examples of that below: 有更好的示例可以实现入站流的缩小,您可以看到以下示例:

Additionally I found a really nice project that supports all of this on github. 另外,我发现了一个非常好的项目,它支持github上的所有这些。

Note while I arrived to this answer by myself Simon in your comments suggested this approach 2 days ago from the date of this answer. 请注意,当我自己到达这个答案时,Simon在你的评论中提出了这个方法,从这个答案之日起2天前。

One Solution without editing any IIS Setting or Installing any Nuget package is to add a MessageHandler to your WEB API. 没有编辑任何IIS设置或安装任何Nuget包的一个解决方案是将MessageHandler添加到您的WEB API。

This will catch requests with the "AcceptEncoding" Header and compress them using the Build in System.IO.Compression libraries. 这将使用“AcceptEncoding”标头捕获请求,并使用Build in System.IO.Compression库对其进行压缩。

public class CompressHandler : DelegatingHandler
{
    private static CompressHandler _handler;
    private CompressHandler(){}
    public static CompressHandler GetSingleton()
    {
        if (_handler == null)
            _handler = new CompressHandler();
        return _handler;
    }
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        return base.SendAsync(request, cancellationToken).ContinueWith<HttpResponseMessage>((responseToCompleteTask) =>
        {
            HttpResponseMessage response = responseToCompleteTask.Result;
            var acceptedEncoding =GetAcceptedEncoding(response);
            if(acceptedEncoding!=null)
                response.Content = new CompressedContent(response.Content, acceptedEncoding);

            return response;
        },
        TaskContinuationOptions.OnlyOnRanToCompletion);
    }
    private string GetAcceptedEncoding(HttpResponseMessage response)
    {
        string encodingType=null;
        if (response.RequestMessage.Headers.AcceptEncoding != null && response.RequestMessage.Headers.AcceptEncoding.Any())
        {
            encodingType = response.RequestMessage.Headers.AcceptEncoding.First().Value;
        }
        return encodingType;
    }


}

    public class CompressedContent : HttpContent
{
    private HttpContent originalContent;
    private string encodingType;

    public CompressedContent(HttpContent content, string encodingType)
    {
        if (content == null)
        {
            throw new ArgumentNullException("content");
        }

        if (encodingType == null)
        {
            throw new ArgumentNullException("encodingType");
        }

        originalContent = content;
        this.encodingType = encodingType.ToLowerInvariant();

        if (this.encodingType != "gzip" && this.encodingType != "deflate")
        {
            throw new InvalidOperationException(string.Format("Encoding '{0}' is not supported. Only supports gzip or deflate encoding.", this.encodingType));
        }

        // copy the headers from the original content
        foreach (KeyValuePair<string, IEnumerable<string>> header in originalContent.Headers)
        {
            this.Headers.TryAddWithoutValidation(header.Key, header.Value);
        }

        this.Headers.ContentEncoding.Add(encodingType);
    }

    protected override bool TryComputeLength(out long length)
    {
        length = -1;

        return false;
    }

    protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
    {
        Stream compressedStream = null;

        if (encodingType == "gzip")
        {
            compressedStream = new GZipStream(stream, CompressionMode.Compress, leaveOpen: true);
        }
        else if (encodingType == "deflate")
        {
            compressedStream = new DeflateStream(stream, CompressionMode.Compress, leaveOpen: true);
        }

        return originalContent.CopyToAsync(compressedStream).ContinueWith(tsk =>
        {
            if (compressedStream != null)
            {
                compressedStream.Dispose();
            }
        });
    }
}

And add this handler to your Global.asax.cs 并将此处理程序添加到Global.asax.cs

GlobalConfiguration.Configuration.MessageHandlers.Insert(0, CompressHandler.GetSingleton());

Kudos to Ben Foster. 感谢Ben Foster。 ASP.NET Web API Compression ASP.NET Web API压缩

Just an addendum to enabling compression in IIS via the applicationHost.config file. 只是通过applicationHost.config文件在IIS中启用压缩的附录。

Use the IIS config manager to make the changes or notepad.exe to edit the file. 使用IIS配置管理器进行更改或使用notepad.exe编辑文件。 I was using Notepad++ and even though the file was saving, it actually was not. 我使用的是Notepad++ ,即使该文件正在保存,但实际上并非如此。

Something to do with 32/64bit environments, configs and the programs that edit them. 与32/64位环境,配置和编辑它们的程序有关。 Ruined my afternoon!! 毁了我的下午!!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 返回HttpResponseMessage并配置Application Insights时,WebAPI响应永远不会完成 - WebAPI Response never completes when returning HttpResponseMessage and Application Insights is configured 使用WebAPI HttpResponseMessage时下载文件提示 - Download file prompt when using WebAPI HttpResponseMessage 返回Task时,DelegatingHandler挂起<HttpResponseMessage> - DelegatingHandler hangs when returning Task<HttpResponseMessage> 为什么当我通过 webapi 或直接调用方法时 HttpResponseMessage 不一样 - Why HttpResponseMessage is not the same when I call Method throught webapi or directly ProtoBuf在WebApi HttpResponseMessage中序列化异常 - ProtoBuf serialize exception in WebApi HttpResponseMessage WCF Restful返回HttpResponseMessage想要在设置内容时进行协商 - WCF Restful returning HttpResponseMessage wants to negotiate when setting content 返回HTTP 403.7 HTTPResponseMessage - Returning an HTTP 403.7 HTTPResponseMessage GetAsync:不返回HttpResponseMessage - GetAsync : not returning HttpResponseMessage 返回带有批处理响应的HttpResponseMessage - Returning an HttpResponseMessage with a batch response HttpResponseMessage不返回任何响应? - HttpResponseMessage Not Returning any response?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM