C#使用GET方法发送HTTP Body请求

7
我正在使用一个API,需要我将方法设置为GET并包括一个消息体。但是当我尝试这样做时,我收到以下错误消息:“Cannot send a content-body with this verb-type”。我了解到HttpWebRequest类不支持此操作,并且这是异常的原因。是否有解决方法?
以下是我的当前代码:数据是作为字节数组编码的JSON字符串。
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
request.Method = "GET";
request.ContentType = "application/json";
request.ContentLength = data.Length;
using (Stream requestStream = request.GetRequestStream()) {
    requestStream.Write(data.ToArray(), 0, (int)data.Length);
}

这是我试图仿效的PHP代码

<?php
$data = array("id" => "1234");
$data_string = json_encode($data);
$ch = curl_init('url');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET");
curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
    'Content-Type: application/json',
    'Content-Length: ' . strlen($data_string))
);
$result = curl_exec($ch);
var_dump($result);
?>

谢谢您,

3个回答

2
一个违反REST的API应该被叫做什么?“HASTE”?“DISQUIET”?希望这个服务只是不在意动词,而PHP代码使用了GET并且碰巧遇到了服务器没有阻止它的bug。这是一个非常小的bug,只要它表现正确就没问题,并且POST方式也能正常运行。
如果以上方法都失败了,你最好找看看是否有另一种方法来处理读取请求(如果自然符合GET),可以通过URI传递参数,或者根据RFC 2616使用适当的标头。或者通过POST、GET等方式接受一些东西。
如果这些方法都行不通,你将不得不在TcpClient上构建HTTP客户端,这将非常可怕。

我开始觉得我必须走这条路。但我会尝试询问他们是否可以接受URI中的参数。对我来说,这似乎非常简单。 - Fernando
1
好吧,我再也不会抱怨我必须忍受的REST违规行为了。不,那是个谎言,我总是会抱怨它们,但是坚持要求GET带有body是我见过的最糟糕的情况。(你确认它拒绝使用POST处理相同的数据,对吗?) - Jon Hanna
是的,由于POST用于执行实体的更新操作,并且仅返回操作的状态以及如果操作失败则返回一个JSON对象作为消息。我会尝试说服API开发人员修改他们的GET方法,否则我将不得不使用TcpClient创建一个单独的调用。谢谢。 - Fernando
@JonHanna - 我认为在 GET 方法中发布正文也很愚蠢; 不幸的是, 我认为一些高级 Elasticsearch 6.6 _search 示例当前在使用 GET 方法时通过正文传递。我需要在 ASP.NET 中模拟这些示例。HttpWebRequest 并没有让我这样做,而且我没有轻松访问 CURL: https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-match-all-query.html - Shawn Eary
我测试了HttpWebRequest、WebRequest和HttpClient。如果在GET请求中包含内容主体,它们都会返回异常。对于这个问题,C#并不是你的朋友。我不知道你的系统是如何构建的。是否可以将API请求移动到网站上,并使用JavaScript进行请求?JavaScript将允许您在GET请求中包含内容主体。 - Jason Williams
Snark从2012年开始就有意义了,但现在看来是时候准备接受带有请求体的GET作为RESTful了--这里有一个详细的RFC更改讨论。无论如何,正如ShawnEary所说,对于带有请求体的GET,存在有效且非平凡的用例,无论我们是否认为它应该是RESTful。我敢打赌@IanKemp的笔记,Core(也称.NET 5)现在支持带有请求体的GET不是微软的错误。2¢ - ruffin

2

2
-1 我尝试过了。base > System.Net.ProtocolViolationException > base > System.InvalidOperationException > “无法在方法为:GET 时发送数据”。 - minnow
@minnow 感谢您提醒我这个问题 - 我在测试代码后过了一段时间才发布答案,所以我忘记了我只能让它与Core一起工作 - 我已经修改了它来注意到这一点。 - Ian Kemp
这不被支持的原因是你不应该在 GET 请求中发送 body 数据,整个请求应该是 URL 和头部指引服务器,body 应该被忽略。当然了,一个有问题的 Web 服务需要有问题的客户端,但从一般意义上来说,询问如何在 GET 请求中发送 body 数据完全是错误的问题。 - Lasse V. Karlsen
3
HTTP规范中并没有明确禁止使用无需请求主体的动词发送带有请求主体的请求。我个人认为,某些情况下这样做是有效的,特别是如果您的请求非常大(太大而无法在URL中编码所有参数)-例如Elasticsearch。现在,你可能会说“在那种情况下,使用POST”,但从RESTful的角度来看,POST语义上是用来更新资源,而GET则是用来获取资源。总之,我不想在评论中卷入一个语义/宗教战争。 - Ian Kemp
2
GET在HTTP规范中被记录为检索由“请求URI”标识的信息,其中不包括正文。我理解并知道很多服务“滥用”(我的观点,不需要回应)GET请求并发送大量数据,但实际上这不在规范中。显然,从技术上讲是可以做到的,我也看到了很多使用它的服务,但这不符合规范。但是,没必要就此展开一场大讨论,毕竟这只是一个观点问题 :) - Lasse V. Karlsen
1
这是一个建议不要这样做的事情,因为它不是 SPEC 的一部分。这就是为什么 .net core 实现了它,因为有需要,并且从语义上讲,发送 POST 请求来获取数据是没有意义的。但 mono runtime 没有这样做,所以显然这是一个偏好问题。 - Karan Harsh Wardhan

-3

4
谢谢。我理解了,但这不是我的能力范围。这是一个第三方API,我需要使用它。 - Fernando
7
你知道ElasticSearch的REST API是如何工作的吗?发送复杂查询需要一个请求体和一个GET请求。从概念上讲,这非常符合REST的工作方式。任何其他谓词都是错误的,而且你不能总是将复杂查询压缩到URL中。我很惊讶HttpWebRequest对象无法处理这个。 - lasseschou

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接