繁体   English   中英

HttpClient.SendAsync 不发送请求正文

[英]HttpClient.SendAsync not sending request body

我正在使用 .NET 4.0 的 ASP.NET Web API 客户端库(Microsoft.AspNet.WebApi.Client 版本 4.0.30506.0)。

我需要发送一个带有请求正文的 HTTP DELETE。 我已将其编码如下:

using (var client = new HttpClient())
{
    client.BaseAddress = Uri;
    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

    // I would normally use httpClient.DeleteAsync but I can't because I need to set content on the request.
    // For this reason I use httpClient.SendAsync where I can both specify the HTTP DELETE with a request body.
    var request = new HttpRequestMessage(HttpMethod.Delete, string.Format("myresource/{0}", sessionId))
      {
        var data = new Dictionary<string, object> {{"some-key", "some-value"}};
        Content = new ObjectContent<IDictionary<string, object>>(data, new JsonMediaTypeFormatter())
      };
    var response = await client.SendAsync(request);
    // code elided
}

对于 Fiddler,请求正文永远不会被序列化:

DELETE http://localhost:8888/myApp/sessions/blabla123 HTTP/1.1 Accept: application/json Content-Type: application/json; charset=utf-8 Host: localhost:8888 Content-Length: 38 Expect: 100-continue

来自服务器的响应:

HTTP/1.1 408 Request body incomplete Date: Sun, 10 Aug 2014 17:55:17 GMT Content-Type: text/html; charset=UTF-8 Connection: close Cache-Control: no-cache, must-revalidate Timestamp: 13:55:17.256 The request body did not contain the specified number of bytes. Got 0, expected 38

我尝试了许多解决方法,包括将被序列化的类型更改为其他类型,自己使用 JsonSerialize 进行序列化,将 HTTP DELETE 更改为 PUT 等......

没有任何效果。 任何帮助将非常感激。

我解决了这个问题,虽然没有意义。 我注意到,如果我将调用更改为HTTP PUT或POST,它仍然无法将内容序列化为请求主体。 这很奇怪,因为以前的PUT和POST都很成功。 在对框架库进行了大量调试之后(使用Reflector),我终于找到了唯一一个“不同”的东西。

我正在使用NUnit 2.6.2。 我的测试结构是:

[Test]
async public void Test()
{
  // successful HTTP POST and PUT calls here
  // successful HTTP DELETE with request body here (after 
  //       moving it from the TearDown below)
}

[TearDown]
async public void TerminateSession()
{
  // failed HTTP DELETE with request body here
}

为什么这在TearDown中失败但在Test本身中没有? 我不知道。 TearDown属性或使用async关键字是否正在进行(因为我等待异步调用)?

我不确定是什么导致了这种行为,但我现在知道我可以提交一个带有请求体的HTTP DELETE(如我在问题中的代码示例中所述)。

另一个有效的解决方案如下:

[Test]
async public void Test()
{
  // create and use an HttpClient here, doing POSTs, PUTs, and GETs
}

// Notice the removal of the async keyword since now using Wait() in method body
[TearDown]
public void TerminateSession()
{
  // create and use an HttpClient here and use Wait().
  httpClient.SendAsync(httpRequestMessage).Wait();
}

我知道说“不要这样做”从来没有那么有用,但在这种情况下,我认为将调用拆分为DELETE后跟或之前的POST或PUT是有意义的。

HTTP RFC没有明确地就此问题发表意见,因此从技术上讲,它意味着我们可以 然而,另一个问题是我们应该这样做。

在这种情况下,我会寻找其他实现来看看事实上的标准是什么。 正如您在.net实现中发现的那样,设计人员似乎不希望发送带有DELETE调用的正文。 那么,让我们看看另一个流行(并且非常不同的impl)Python 请求

>>> r = requests.delete(url=url, auth=auth)
>>> r.status_code
204
>>> r.headers['status']
'204 No Content'

其他人没有身体。 因此,如果规范作者没有提到它,并且流行的实现假设没有正文,那么最小惊喜的原则意味着我们也不应该这样做。

因此,如果您可以更改API,则API的客户端将更容易分成两个调用。 否则,您可能不得不求助于自定义hackery将正文填入DELETE调用。

好消息是,您可能在.net框架中发现了一个错误,这本身就是一项成就。 在没有实际发送内容的情况下宣传非零内容长度的客户端已被破坏。

万一其他人遇到这个问题,我注意到可能导致这种情况的一件事是,如果您设置了一个带有换行符的标题。

我们有一个加密的 OAuth 令牌,它在运行时被解密并设置为应用程序上的 OAuth 标头。 换行符已加密到令牌中,因此查看配置或它在那里的任何内容并不明显,但是如果您这样做:

var message = new HttpRequestMessage(HttpMethod.Post, "https://example.com");
message.Headers.ContentType = new MediaTypeHeaderValue("application/json");
message.Content = new StringContent("{ \"someKey\": \"someValue\" }", Encoding.UTF8);

// note the trailing newline
message.Headers.Authorization = new AuthenticationHeaderValue("OAuth", "my auth token\n");

var response = await httpClient.SendAsync(request);

将发送 HTTP 请求,但不会随其发送内容。 发生这种情况时不会抛出任何异常,如果您检查 HttpRequestMessage,内容将显示在那里,但实际上并没有通过网络发送。

这发生在 Windows 和 Linux 上的 .NET 5 中,我没有在其他框架版本/平台上测试过。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM