Web API作为代理和分块传输编码

时间:2013-05-24 13:32:48

标签: asp.net .net asp.net-web-api

我一直在使用Web API(Web Host)作为代理服务器,并且遇到了我的Web API代理如何使用“Transfer-Encoding:chunked”标头处理响应的问题。

绕过代理时,远程资源会发送以下响应标头:

Cache-Control:no-cache
Content-Encoding:gzip
Content-Type:text/html
Date:Fri, 24 May 2013 12:42:27 GMT
Expires:-1
Pragma:no-cache
Server:Microsoft-IIS/8.0
Transfer-Encoding:chunked
Vary:Accept-Encoding
X-AspNet-Version:4.0.30319
X-Powered-By:ASP.NET

在浏览基于Web API的代理时,除非我将响应标头上的TransferEncodingChunked属性显式重置为false,否则我的请求会挂起:

response.Headers.TransferEncodingChunked = false;

我承认,我并不完全了解设置TransferEncodingChunked属性有什么影响,但我觉得奇怪的是,为了使代理按预期工作,我需要在明确传入响应时将此属性设置为false有一个“Transfer-Encoding:chunked”标题。我也担心明确设置此属性的副作用。任何人都可以帮助我了解发生了什么以及为什么需要设置此属性?

更新:所以我在进行代理与非代理时,更多地挖掘了响应的差异。无论我是否将TransferEncodingChunked属性显式设置为false,通过代理时的响应标头与不通过代理时的响应标头完全相同。但是,响应内容不同。以下是一些示例(我关闭了gzip编码):

// With TransferEncodingChunked = false
2d\r\n
This was sent with transfer-encoding: chunked\r\n
0\r\n

// Without explicitly setting TransferEncodingChunked
This was sent with transfer-encoding: chunked

显然,将TransferEncodingChunked设置为false发送的内容实际上是传输编码的。这实际上是正确的响应,因为它是从代理后面的请求资源收到的。继续奇怪的是第二种情况,我没有在响应上明确设置TransferEncodingChunked(但它在从代理服务接收的响应头中)。很明显,在这种情况下,尽管事实上是响应,但响应实际上并不是由IIS进行传输编码的。奇怪...这开始感觉像设计行为(在这种情况下,我很想知道如何/为什么)或IIS,ASP.Net或Web API中的错误。

以下是我正在运行的代码的简化版本:

代理Web API应用程序:

// WebApiConfig.cs
config.Routes.MapHttpRoute(
    name: "Proxy",
    routeTemplate: "{*path}",
    handler: HttpClientFactory.CreatePipeline(
        innerHandler: new HttpClientHandler(), // Routes the request to an external resource
        handlers: new DelegatingHandler[] { new ProxyHandler() }
    ),
    defaults: new { path = RouteParameter.Optional },
    constraints: null
);

// ProxyHandler.cs
public class ProxyHandler : DelegatingHandler
{
    protected override async System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
    {
        // Route the request to my web application
        var uri = new Uri("http://localhost:49591" + request.RequestUri.PathAndQuery);
        request.RequestUri = uri;

        // For GET requests, somewhere upstream, Web API creates an empty stream for the request.Content property
        // HttpClientHandler doesn't like this for GET requests, so set it back to null before sending along the request
        if (request.Method == HttpMethod.Get)
        {
            request.Content = null;
        }

        var response = await base.SendAsync(request, cancellationToken);

        // If I comment this out, any response that already has the Transfer-Encoding: chunked header will hang in the browser
        response.Headers.TransferEncodingChunked = false;

        return response;
    }
}

我的Web应用程序控制器创建了一个“分块”响应(也是Web API):

public class ChunkedController : ApiController
{
    public HttpResponseMessage Get()
    {
        var response = Request.CreateResponse(HttpStatusCode.OK);

        var content = "This was sent with transfer-encoding: chunked";
        var bytes = System.Text.Encoding.ASCII.GetBytes(content);
        var stream = new MemoryStream(bytes);

        response.Content = new ChunkedStreamContent(stream);

        return response;
    }
}

public class ChunkedStreamContent : StreamContent
{
    public ChunkedStreamContent(Stream stream)
        : base(stream) { }

    protected override bool TryComputeLength(out long length)
    {
        length = 0L;
        return false;
    }
}

1 个答案:

答案 0 :(得分:6)

从HttpClient的角度来看,内容分块本质上是传输的细节。响应.Content提供的内容总是由HttpClient为您解除阻塞。

看起来Web API中存在一个错误,当在IIS上运行时,响应请求时,它没有正确(重新)响应内容.Headers.TransferEncodingChunked属性。所以问题是代理通过标题告诉客户端内容被分块,而事实上并非如此。我在这里提交了错误: https://aspnetwebstack.codeplex.com/workitem/1124

我认为您的解决方法目前是最佳选择。

另请注意,此处有多个图层可能未针对代理方案进行设计/测试(并且可能不支持它)。在HttpClient方面,请注意它将自动解压缩并遵循重定向,除非您关闭该行为。至少,您需要设置以下两个属性: http://msdn.microsoft.com/en-us/library/system.net.http.httpclienthandler.allowautoredirect.aspx http://msdn.microsoft.com/en-us/library/system.net.http.httpclienthandler.automaticdecompression.aspx

在WebApi / IIS方面,您至少发现了一个错误,找到其他错误并不令人惊讶。只是预先警告可能存在这样的错误,目前正在使用这些技术在其主要设计用例之外编写代理。