HttpClient.SendAsync不发送请求正文

时间:2014-08-10 18:07:08

标签: c# json asp.net-web-api http-delete

我正在使用适用于.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
}

Per 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等等。

没有任何效果。任何帮助将不胜感激。

2 个答案:

答案 0 :(得分:2)

我解决了这个问题,虽然没有意义。我注意到,如果我将调用更改为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();
}

答案 1 :(得分:1)

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

HTTP RFC没有明确说明问题,因此从技术上讲,这意味着我们可以。另一个问题是应该我们这样做。

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

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

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

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

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