HttpResponseMessage
を返すWebAPIコントローラーがあり、gzip圧縮を追加したい。これはサーバーコードです。
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);
}
そして、これはjqueryを使用したajax呼び出しのクライアントコードです。
$.ajax({
url: "/SomeRoute",
type: "POST",
cache: "false",
data: SomeData,
beforeSend: function (jqXHR) { jqXHR.setRequestHeader('Accept-Encoding', 'gzip'); },
success: function(msg) { ... }
これを実行すると、サーバーコードはバグなしで戻りますが、クライアントのバグは次のようになります。
(failed)
net::ERR_CONTENT_DECODING_FAILED
私がフィドラーで見ると、これは私が見るものです:
クライアントが通常処理するgzip圧縮されたコンテンツをWebサービスが返すようにするには、何を変更する必要がありますか? HttpModuleを使用して、またはIISの設定を介してこれを行うこともできますが、どちらのオプションもホスティングのシナリオに適合しません。
(ホスティング)にアクセスできないため、IIS設定を探していないことに注意してください。
IIS configurationへのアクセス権がある場合
ヘッダーを適用して、gzip圧縮されることを期待することはできません。応答は圧縮されません。
追加したヘッダーを削除し、IISサーバーで動的圧縮と静的コンテンツ圧縮が有効になっていることを確認する必要があります。
コメンターの1人は、その方法を示すstakoverflowの優れたリソースリンクについて言及しています。
動的圧縮が既にインストールされている場合(IISのデフォルトのインストールではない場合)、web.configの値を設定するだけで機能することに注意してください。
これに関する情報は、MSDNドキュメントで見つけることができます。 http://www.iis.net/configreference/system.webserver/httpcompression
単純な圧縮
以下は、独自の圧縮を行う簡単な例を使用しています。この例では、Visual StudioプロジェクトテンプレートのWeb Api MVC 4プロジェクトを使用しています。 HttpResponseMessagesで圧縮を機能させるには、カスタムMessageHandlerを実装する必要があります。以下の実例を参照してください。
以下のコード実装を参照してください。
メソッドをあなたの例と同じように維持しようとしたことに注意してください。
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();
}
});
}
}
}
}
また、アプリの構成に新しいメッセージハンドラーを追加します。
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();
}
}
}
カスタムハンドラは-Kiran Challa( http://blogs.msdn.com/b/kiranchalla/archive/2012/09/04/handling-compression-accept-encoding-sample.aspx によってまとめられました=)
インバウンドストリームのデフレートを実装するより良い例もあります。以下の例をご覧ください。
さらに、githubでこのすべてをサポートする本当に素晴らしいプロジェクトを見つけました。
私が自分でこの回答にたどり着いた間、あなたのコメントでサイモンはこの回答の日付から2日前にこのアプローチを提案しました。
これらのNuGetパッケージを追加します。
Microsoft.AspNet.WebApi.Extensions.Compression.Server System.Net.Http.Extensions.Compression.Client
次に、1行のコードをApp_Start\WebApiConfig.cs
に追加します。
GlobalConfiguration.Configuration.MessageHandlers.Insert(0, new ServerCompressionHandler(new GZipCompressor(), new DeflateCompressor()));
これでうまくいきます!
詳細:
お役に立てば幸いです。
** @ JCisarからのコメントの後に更新
ASP.Net Coreの更新
Nugetパッケージは
Microsoft.AspNetCore.ResponseCompression
1つのソリューションwithoutany IIS設定またはインストールNugetパッケージは、Web APIにMessageHandlerを追加します。
これは、「AcceptEncoding」ヘッダーでリクエストをキャッチし、Build inSystem.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();
}
});
}
}
このハンドラーをGlobal.asax.csに追加します
GlobalConfiguration.Configuration.MessageHandlers.Insert(0, CompressHandler.GetSingleton());
ベン・フォスターへの称賛。 ASP.NET Web API圧縮
applicationHost.config
ファイルを介してIISで圧縮を有効にするための補遺です。
IIS config manager を使用して変更を行うか、notepad.exe
を使用してファイルを編集します。Notepad++
を使用していましたが、ファイルが保存されていても実際には保存されませんでした。
32/64ビット環境、設定、およびそれらを編集するプログラムに関係します。午後を台無しに!!