【问题标题】:Returning anything but ok in HttpResponseMessage results in serverside webservice call delays在 HttpResponseMessage 中返回除 ok 之外的任何内容会导致服务器端 Web 服务调用延迟
【发布时间】:2018-06-08 09:01:34
【问题描述】:

我正在从 .Net 3.5 Framework 客户端调用 2 个 Web 服务(服务器是更现代的 .NET Framework Server:4.6)。

分离后我没有遇到任何问题,但是如果我按照下面显示的顺序调用方法,我会遇到从未输入服务器上的 VerifyFile 方法的问题,而是立即得到一个 服务器在客户端上提交了协议 Violation Section=ResponseStatusLine 错误。 更准确地说:服务器在事件中注册了 VerifyFile 请求,但直到 6 分钟后才输入实际代码(并立即返回导致上述错误的内容)。

经过大量测试,我可以将其简化为第一种方法“DownloadFile”是问题的原因。并且总是当我从它返回状态码以外的任何其他内容时(内容与否都无关紧要)。

我对这种现象完全不知所措(我原以为客户端会遇到麻烦,但是服务器直到几分钟后才输入那个代码部分看起来服务器本身也遇到了麻烦,这出乎意料,同样出人意料的是第二个方法而不是原来的方法在那里遇到了问题)。

所以我的问题是为什么返回除了 HttpStatusCode.OK 之外的任何内容都会导致这些问题,我可以做些什么来纠正这个问题?

客户:

WebClient webClient = new WebClient();
webClient.QueryString.Add("downloadId", id);
webClient.DownloadFile("localhost/Download/DownloadFile", @"c:\temp\local.txt");
webClient = new WebClient();
webClient.QueryString.Add("downloadId", id);
webClient.QueryString.Add("fileLength", GetFileLength(@"c:\temp\local.txt"));
var i = webClient.DownloadString("localhost/Download/VerifyFile");

Testwise 我将下载文件替换为:webClient.DownloadString("localhost/Download/DownloadFile");

最初我也只有一个新的 WebClient,但在第一次失败后添加了第二个。

服务器:

[RoutePrefix("Download")]
public class DownloadController : ApiController
{
    [HttpGet]
    [Route("DownloadFile")]
    public IHttpActionResult DownloadFile(int downloadId){
        return ResponseMessage(GetFile(downloadId));
    }

    private HttpResponseMessage GetFile(int downloadId){
        HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);

        string filePath = GetFilePathFromDB(downloadid);

        if (filePath != String.Empty){
            var stream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite);
            result.Content = new StreamContent(stream);
            result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
            result.Content.Headers.ContentDisposition.FileName = Path.GetFileName(filePath);
            result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
            result.Content.Headers.ContentLength = stream.Length;   
        }
        else{
            result = new HttpResponseMessage(HttpStatusCode.InternalServerError);
        }       
        return result;
    }

    [HttpGet]
    [Route("VerifyFile")]
    public IHttpActionResult VerifyFile(int downloadId, int fileLength)
    {
        return Content(HttpStatusCode.OK, "OK");
    }


    private string GetFilePathFromDB(int downloadId)
    {
         return @"C:\temp\mytest.txt"; // testcode
    }
}

【问题讨论】:

  • @Thomas 我怀疑第二个操作是在上一次调用可用之前尝试访问文件流。
  • @Nkosi 我今天发现了发生了什么以及如何纠正它,但老实说,我无法找出原因。当我用 return Request.CreateErrorResponse(HttpStatusCode.ExpectationFailed, "Some message here"); 替换错误返回时问题停止了,一切都按预期工作。
  • @Thomas Not Found 也会发生同样的情况吗?
  • @Thomas,您正在创建一个没有内容且没有内容类型的响应,这可能是这里的问题吗?

标签: c# asp.net


【解决方案1】:

你可以尝试三件事。

  1. 遵循 http 规范,以免出现此错误

  2. 将此添加到您的客户端 web.config

    <system.net>
       <settings>
         <httpWebRequest useUnsafeHeaderParsing="true" />
       </settings>
    </system.net>
    
  3. 将连接头设置为关闭而不是保持活动

    class ConnectionCloseClient : WebClient
    {
        protected override WebRequest GetWebRequest(Uri address)
        {
            WebRequest request = base.GetWebRequest(address);
            if (request is HttpWebRequest)
            {
                (request as HttpWebRequest).KeepAlive = false;
            }
            return request;
        }
    }
    

【讨论】:

    【解决方案2】:

    我的理论是,当您从服务器获得错误响应时,不会创建文件。假设您正在捕获 DownloadFile 异常,当您返回任何其他响应而不是 OK 时应该收到该异常。

    GetFileLength 可能返回一个无效数字,根据这个answer 它可能会导致您提到的错误。至于为什么需要 6 分钟才能到达该服务器代码 - 我猜它在调用该方法之前做了一些内部错误处理,并返回一个错误。遗憾的是 ASP.net 4.* 不是开源的,所以我不太熟悉内部工作原理。

    【讨论】:

      【解决方案3】:

      我认为这是Keep-Alive 标头的问题。您可以捕获 http 请求并检查此值。 true 值将尝试保持打开的连接。并非所有代理都与此标头兼容。

      尝试使用两个不同的WebClient 实例并在使用下一个实例之前将它们处理掉。或者强制标头为假。

      WebClient webClient = new WebClient();
      webClient.QueryString.Add("downloadId", id);
      webClient.DownloadFile("localhost/Download/DownloadFile", @"c:\temp\local.txt");
      webClient.Dispose();
      
      webClient2 = new WebClient();
      webClient2.QueryString.Add("downloadId", id);
      webClient2.QueryString.Add("fileLength", GetFileLength(@"c:\temp\local.txt"));
      var i = webClient2.DownloadString("localhost/Download/VerifyFile");
      webClient2.Dispose();
      

      或者将它们包装在 using 语句中:

      using (WebClient webClient = new WebClient())
      {
          webClient.QueryString.Add("downloadId", id);
          webClient.DownloadFile("localhost/Download/DownloadFile", @"c:\temp\local.txt");
      }
      
      using (WebClient webClient = new WebClient())
      {
          webClient2 = new WebClient();
          webClient2.QueryString.Add("downloadId", id);
          webClient2.QueryString.Add("fileLength", GetFileLength(@"c:\temp\local.txt"));
          var i = webClient2.DownloadString("localhost/Download/VerifyFile");
      }
      

      查看这篇文章: WebClient is opening a new connection each time I download a file and all of them stay established

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-12-30
        • 2017-08-17
        • 2014-05-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-15
        相关资源
        最近更新 更多