注意:我的本地服务器是 IISExpress 。我还没有尝试将我的应用程序部署到IIS,因为我无法在本地生成所需的行为。
我正在尝试将已经GZipped客户端的主体发布到我的Web API
我可以通过简单地与IIS Accept-Encoding: gzip
进行通信来获取响应,但发送GZipped请求主体的问题更多。
我尝试通信Content-Encoding: gzip
并将gzipped响应复制粘贴到我的请求主体中(显然这不起作用,因为当我通过我的剪贴板移动时,我通过Unicode传递了二进制文件)。
我尝试<add mimeType="application/json" enabled="true" />
到我服务器上<httpCompression>
配置块内的dynamicTypes列表。
我尝试将此配置复制到应用程序的web.config中的<system.WebServer>
配置块。
我尝试创建一个自定义DelegatingHandler,并添加了一个客户端Fiddler规则,允许我将Unicode json粘贴到我的请求主体中,并让Fiddler在请求发出之前执行GZip压缩。
这些都不允许我的客户端发送GZipped正文以供服务器接收和解压缩。
另请注意:我的POST正文的
对其进行了确认Content-Type
为application/json
。当我发送GZipped BODY(下面的详细信息第2节)时,我收到了关于标题中GZip幻数的错误。这是将一个Fiddler响应中的[复制+粘贴]内容转换为后续的Fiddler请求,因此二进制内容通过剪贴板传递。在将复制到剪贴板时,可能无法转换为Unicode。最相关的部分是它产生一个明确的服务器错误; 服务器凭借响应内部服务器错误
最后注意:使用DelegatingHandler和客户端调用
Utilities.GzipCompress()
,服务器永远不会确认收到请求。如果我注释掉压缩线,服务器会确认请求。
所以这就是让我来到这里的原因:
1]我能够简单地询问Accept-Encoding: gzip
并且IIS通过GZipped响应来表达我的请求。在对<httpCompression>
进行任何配置更改或添加DelegatingHandler之前,我首先尝试将此GZipped内容以不同的请求发送回服务器,同时进行通信Content-Encoding: gzip
当我这样做时,我的应用程序激活了相应的控制器并将我的请求路由到适当的控制器,但Body不会反序列化,表面上是因为它试图将压缩的二进制数据视为明文Content-Type: application/json
。我的客户(Fiddler)收到了400 Bad Request
请注意,为了将gzipped响应链接到我的测试请求的主体,我通过剪贴板传递了二进制数据。我知道这不会“正常工作”(通过剪贴板传递二进制数据通过UTF-8 - 我认为......可能是UTF-16 ......不是非常重要)但我想看看我实际会得到什么样的行为。重要的是IIS和ASP.NET似乎完全忽略了我的
Content-Encoding
标头。
2]然后,我在applicationHost.config中修改了httpCompression
,以便在支持动态压缩的类型列表中包含application/json
和application/json; charset=utf8
<httpCompression directory="%TEMP%\iisexpress\IIS Temporary Compressed Files">
<scheme name="gzip" dll="%IIS_BIN%\gzip.dll" />
<dynamicTypes>
<add mimeType="text/*" enabled="true" />
<add mimeType="message/*" enabled="true" />
<add mimeType="application/javascript" enabled="true" />
<add mimeType="application/atom+xml" enabled="true" />
<add mimeType="application/xaml+xml" enabled="true" />
<add mimeType="application/json" enabled="true" />
<add mimeType="application/json; charset=utf-8" enabled="true" />
<add mimeType="*/*" enabled="false" />
</dynamicTypes>
<staticTypes>
<add mimeType="text/*" enabled="true" />
<add mimeType="message/*" enabled="true" />
<add mimeType="image/svg+xml" enabled="true" />
<add mimeType="application/javascript" enabled="true" />
<add mimeType="application/atom+xml" enabled="true" />
<add mimeType="application/xaml+xml" enabled="true" />
<add mimeType="*/*" enabled="false" />
</staticTypes>
</httpCompression>
我从[ 1 ]重新发出了我的请求,并且在控制器激活之前我收到了500响应:
The magic number in GZip header is not correct. Make sure you are passing in a GZip stream.
无法使用解压缩请求 compressor'System.Net.Http.Extensions.Compression.Core.Compressors.GZipCompressor'
{"Message":"An error has occurred."
,"ExceptionMessage":"Unable to decompress request using compressor'System.Net.Http.Extensions.Compression.Core.Compressors.GZipCompressor'"
,"ExceptionType":"System.Exception"
,"StackTrace":" at Microsoft.AspNet.WebApi.Extensions.Compression.Server.BaseServerCompressionHandler.<DecompressRequest>d__19.MoveNext()\r\n
--- End of stack trace from previous location where exception was thrown ---\r\n
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n
at Microsoft.AspNet.WebApi.Extensions.Compression.Server.BaseServerCompressionHandler.<HandleDecompression>d__16.MoveNext()\r\n
--- End of stack trace from previous location where exception was thrown ---\r\n
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n
at Microsoft.AspNet.WebApi.Extensions.Compression.Server.BaseServerCompressionHandler.<SendAsync>d__15.MoveNext()\r\n
--- End of stack trace from previous location where exception was thrown ---\r\n
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n
at System.Web.Http.HttpServer.<SendAsync>d__0.MoveNext()"
,"InnerException":
{
"Message":"An error has occurred."
,"ExceptionMessage":"The magic number in GZip header is not correct. Make sure you are passing in a GZip stream."
,"ExceptionType":"System.IO.InvalidDataException","StackTrace":" at System.IO.Compression.DeflateStream.EndRead(IAsyncResult asyncResult)\r\n
at System.Threading.Tasks.TaskFactory`1.FromAsyncTrimPromise`1.Complete(TInstance thisRef, Func`3 endMethod, IAsyncResult asyncResult, Boolean requiresSynchronization)\r\n
--- End of stack trace from previous location where exception was thrown ---\r\n
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n
at System.IO.Stream.<CopyToAsyncInternal>d__27.MoveNext()\r\n
--- End of stack trace from previous location where exception was thrown ---\r\n
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n
at System.Net.Http.Extensions.Compression.Core.Compressors.BaseCompressor.<Pump>d__9.MoveNext()\r\n
--- End of stack trace from previous location where exception was thrown ---\r\n
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n
at System.Net.Http.Extensions.Compression.Core.Compressors.BaseCompressor.<Decompress>d__8.MoveNext()\r\n
--- End of stack trace from previous location where exception was thrown ---\r\n
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n
at System.Net.Http.Extensions.Compression.Core.HttpContentOperations.<DecompressContent>d__0.MoveNext()\r\n
--- End of stack trace from previous location where exception was thrown ---\r\n
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n
at Microsoft.AspNet.WebApi.Extensions.Compression.Server.BaseServerCompressionHandler.<DecompressRequest>d__19.MoveNext()"}}
3]我向我的Composition空间引入了一个DelegatingHandler,它只在POST请求时触发。
CompressedRequestHandler.cs
public class CompressedRequestHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request.Method == HttpMethod.Post)
{
if (Compressed(request))
{
request.Content = Decompress(request);
}
}
return base.SendAsync(request, cancellationToken);
}
private bool Compressed(HttpRequestMessage request)
{
if ( request.Content.Headers.ContentEncoding == null) return false;
if (!request.Content.Headers.ContentEncoding.Contains("gzip")) return false;
return true;
}
private HttpContent Decompress(HttpRequestMessage request)
{
Stream output = new MemoryStream();
Task task = request.Content.ReadAsStreamAsync().ContinueWith(t =>
{
Stream input = t.Result;
var gzip = new GZipStream(input, CompressionMode.Decompress);
gzip.CopyTo(output);
gzip.Dispose();
output.Seek(0, SeekOrigin.Begin);
});
task.Wait();
HttpContent compressed = request.Content;
HttpContent decompressed = new StreamContent(output);
foreach(var header in compressed.Headers)
{
decompressed.Headers.Add(header.Key, header.Value);
}
return decompressed;
}
为了确保它是我的请求流中的第一个处理程序,我将其插入到我的Register(HttpConfiguration config)
方法中的索引0
config.MessageHandlers.Insert(0,new CompressedRequestHandler());
我还在Fiddler中引入了一个CustomRule来执行客户端压缩onBeforeRequest,有两个排列(测试规则本身) 1]实际上执行身体的GZip压缩 2]触发规则但不执行压缩
//+================================================================================+
//|[KAB] Trying to have Fiddler compress the clear Body prior to request. |
//| Rationale: POSTing a gzipped response as a Request body is generating |
//| an internal server error |
//| +--------------------------------------------------------------------------+ |
//| |The magic number in GZip header is not correct. Make sure you are passing | |
//| |in a GZip stream. | |
//| +--------------------------------------------------------------------------+ |
//| Goal is to see if passing through the clipboard is what is generating this |
//+================================================================================+
if(oSession.requestBodyBytes != null && (oSession.oRequest.headers.Exists("Client.Request-Encoding")))
{
if(oSession.oRequest["Client.Request-Encoding"] == "gzip")
{
//+=================================================+
//|Commenting these 2 lines prevents the Compression
//|from actually occurring. When I disable compression
//|of requestBodyBytes, the request goes out and is
//|serviced as an uncompressed body by my application
//|-------------------------------------------------
oSession.requestBodyBytes = Utilities.GzipCompress(oSession.requestBodyBytes);
oSession["Content-Length"] = oSession.requestBodyBytes.Length.ToString();
//|-------------------------------------------------
//+=================================================+
oSession["ui-color"] = "green";
oSession["ui-bold"] = "true";
}else{
oSession["ui-color"] = "crimson";
oSession["ui-italics"] = "true";
oSession.oRequest["Client.Request-Encoding.Error"] = "Rule not processed. gzip is the only supported Request-Encoding value";
}
}
关于自定义规则的注释(或重复注意我已经说过的内容)当我注释掉执行
GzipCompress(oSession.requestBodyBytes)
调用的规则的行时,我的应用程序唤醒,我的DelegatingHandler触发并且一切都像请求未压缩一样进行处理。每当启用行时,我的请求都会被GZip压缩(通过Composer确认)但我的应用程序从未收到请求。 DelegatingHandler永远不会触发。