如何在 .net 核心中转发来自远程服务器的响应



我想我已经掉进了兔子洞,并怀疑有一个比我正在尝试的解决方案容易得多的解决方案。基本上在我的 .net 核心 MVC 应用程序中,我想从外部 API 中提取流视频文件。

这意味着,对于对我的操作的任何请求,我想通过网络调用外部 API 函数。我需要向请求添加一个标头,但传递已请求的所有其他标头,然后一旦我收到 API 的回复,我就会将响应完整转发给消费者。它基本上就像没有状态代码的重定向,并向重定向的源添加标头。

我这样做的方式如下。在我的控制器中,我调用 ForwardedResponseResult:

var client = await this.FileAssetsApiContext.GetHttpClient();
//Copy the Headers
foreach (var header in Request.Headers)
{
    header.Value.ToList().ForEach(p => {
        if (client.DefaultRequestHeaders.Contains(header.Key))
        {
            client.DefaultRequestHeaders.Remove(header.Key);
            client.DefaultRequestHeaders.Add(header.Key, p);
         }
         else
         {
             client.DefaultRequestHeaders.Add(header.Key, p);
         }});
    }
    var fullPath = Url.Combine(VideosRootPath, path);
    var response = await client.GetAsync(url);
    return new ForwardedResponseResult(response);     

然后我有一个转发类:

  public class ForwardedResponseResult : ActionResult
{
    public override void ExecuteResult(ActionContext context)
    {
        throw new NotImplementedException();
    }
    public ForwardedResponseResult(HttpResponseMessage httpResponseMessage)
    {
        this.HttpResponseMessage = httpResponseMessage;
    }
    public override async Task ExecuteResultAsync(ActionContext context)
    {
        var responseToCopy = this.HttpResponseMessage;
        var responseToCopyHeaders = responseToCopy.Headers.ToArray();
        var responseToCopyContentHeaders = responseToCopy.Content.Headers.ToArray();
        var streamToCopy = await responseToCopy.Content.ReadAsStreamAsync();
        var responseToReturnTo = context.HttpContext.Response;
        var streamToCopyTo = responseToReturnTo.Body;
        var headers = responseToCopy.Headers.ToArray();
        var contentHeaders = responseToCopy.Content.Headers.ToArray();
        var headersTo = responseToReturnTo.Headers.ToArray();
        foreach (var header in responseToCopy.Headers)
        {
                header.Value.ToList().ForEach(p => responseToReturnTo.Headers.Add(header.Key, p));
        }
        responseToReturnTo.StatusCode = (int) responseToCopy.StatusCode;
        foreach (var header in responseToCopy.Content.Headers)
        {
            header.Value.ToList().ForEach(p => responseToReturnTo.Headers.Add(header.Key, p));
        }
        //Copy to the output buffer
        var remainingBytes = streamToCopy.Length;
        while (remainingBytes > 0)
        {
            //var bufferSize = 1024;
            var bufferSize = 8096;
            var buffLen = remainingBytes > bufferSize ? bufferSize : remainingBytes;
            var buffer = new byte[buffLen];
            streamToCopy.Read(buffer, 0, (int)buffLen);
            streamToCopyTo.Write(buffer, 0, (int)buffLen);
            remainingBytes -= buffLen;
        }
        //streamToCopy.CopyTo(streamToCopyTo);
        //await streamToCopyTo.FlushAsync();
    }
    public HttpResponseMessage HttpResponseMessage { get; }
}

这似乎是巨大的矫枉过正,范围请求的性能很糟糕。我错过了一些明显的东西吗?有没有办法更简单地做到这一点?

我想从外部 API 拉取流视频文件。

在下载量很大的情况下,您希望确保使用 HttpCompletionOption.ResponseHeadersRead .否则,GetAsync调用实际上是将整个响应内容加载到内存中(并且.Content.ReadAsStreamAsync实际上不是异步的(。

此外,您还可以使用现有的FileStreamResult类型。这样的事情应该有效:

var client = await this.FileAssetsApiContext.GetHttpClient();
... // Copy Request.Headers to client.DefaultRequestHeaders.
var fullPath = Url.Combine(VideosRootPath, path);
var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
var stream = await client.Content.ReadAsStreamAsync();
... // Copy response.Headers / response.Content.Headers to Response.Headers
return File(stream, "application/octet-stream");

我不知道复制请求/响应标头的干净方法,但它似乎是几种实用程序方法的好地方。

最新更新