C# HttpClient.SendAsync未发送请求正文

C# HttpClient.SendAsync未发送请求正文,c#,json,asp.net-web-api,http-delete,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.Defaul

我正在使用.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
}
[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();
}
使用(var-client=new-HttpClient())
{
client.BaseAddress=Uri;
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(新的MediaTypeWithQualityHeaderValue(“应用程序/json”);
//我通常会使用httpClient.DeleteAsync,但我不能,因为我需要在请求上设置内容。
//出于这个原因,我使用httpClient.sendaync,在这里我可以用请求体指定HTTP DELETE。
var request=newhttprequestmessage(HttpMethod.Delete,string.Format(“myresource/{0}”,sessionId))
{
var data=新字典{{“某个键”,“某个值”};
内容=新的ObjectContent(数据,新的JsonMediaTypeFormatter())
};
var response=wait client.sendaync(请求);
//代码省略
}
对于Fiddler,请求正文永远不会序列化:

删除http://localhost:8888/myApp/sessions/blabla123 HTTP/1.1
接受:application/json
内容类型:application/json;字符集=utf-8
主机:localhost:8888
内容长度:38
期望值:100继续

来自服务器的响应:

HTTP/1.1 408请求正文不完整
日期:2014年8月10日星期日17:55:17 GMT
内容类型:text/html;字符集=UTF-8
连接:关闭
缓存控制:没有缓存,必须重新验证
时间戳:13:55:17.256
请求正文不包含指定的字节数。得到0,预期为38

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


什么都没用。任何帮助都将不胜感激。

我知道说“不要那样做”永远不会有多大帮助,但在这种情况下,我认为将调用拆分为删除,然后再添加一个POST或PUT是有意义的

政府对此事没有明确的意见,所以从技术上讲,这意味着我们可以。然而,另一个问题是我们是否应该这样做

在这种情况下,我会寻找其他实现,以了解什么是事实上的标准。正如您在.net实现中所发现的那样,设计者似乎并不希望在DELETE调用中发送正文。那么,让我们看看另一种流行的(也是非常不同的impl)Python:

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

因此,如果您可以更改API,那么API的客户端将更容易拆分为两个调用。否则,您可能不得不求助于自定义黑客,将主体塞进删除调用中


好消息是您可能在.net框架中发现了一个bug,这本身就是一项成就。没有实际发送内容长度为非零的客户机被破坏。

我解决了这个问题,尽管它没有意义。我注意到,如果我将调用更改为HTTPPUT或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属性或async关键字的使用是否有问题(因为我等待异步调用)

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

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

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
}
[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();
}

什么是
ObjectContent
类型?顺便问一下,您是否尝试过在未将内容类型设置为
application json
的情况下使用
deleteAncy
?ObjectContent泛型类型是IDictionary。本质上是一组键值对。DeleteAsync不支持传递任何内容。我将HttpRequestMessage的content属性设置为
new ObjectContent(数据,new JsonMediaTypeFormatter())
。我确实尝试了StringContent,但也不起作用。当我序列化我的测试数据以放入字符串时,它看起来是这样的
{“some key”,“some value”}
同意,规范是可以解释的。在某个地方有一只虫子,尽管它可能在努尼特。