C# 如何使用HttpClient发送GET请求主体中的内容?

C# 如何使用HttpClient发送GET请求主体中的内容?,c#,get,request,dotnet-httpclient,C#,Get,Request,Dotnet Httpclient,目前,为了向API接口发送参数化GET请求,我正在编写以下代码: api/master/city/filter?cityid=1&citycode='ny' var client = new RestClient("some url"); var request = new RestRequest(Method.GET); foreach (var d in myData) request.AddParameter(d.Key, d.Value); var

目前,为了向API接口发送参数化GET请求,我正在编写以下代码:

api/master/city/filter?cityid=1&citycode='ny'
var client = new RestClient("some url");
var request = new RestRequest(Method.GET);
foreach (var d in myData)
    request.AddParameter(d.Key, d.Value);
var result = (await client.ExecuteAsync(request)).Content;
但是我看到URL的长度有2083个字符的限制

为了避免这种情况,我希望在GET请求的内容体中以json格式发送参数

但是,我发现HttpClient的Get方法都不允许发送内容体。在我看到的帖子中,HttpClient中有一个名为PostAsync的方法,它允许内容主体


为了避免URL长度限制,有没有一种方法可以发送不在URL中的GET请求的参数?

请阅读此答案末尾的注意事项,了解通常不建议使用正文的HTTP GET请求的原因


  • 如果您使用的是.NET核心,则该标准可以立即执行此操作。例如,要发送带有JSON正文的GET请求,请执行以下操作:

      HttpClient client = ...
    
      ...
    
      var request = new HttpRequestMessage
      {
          Method = HttpMethod.Get,
          RequestUri = new Uri("some url"),
          Content = new StringContent("some json", Encoding.UTF8, ContentType.Json),
      };
    
      var response = await client.SendAsync(request).ConfigureAwait(false);
      response.EnsureSuccessStatusCode();
    
      var responseBody = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
    
  • .NETFramework不支持这种开箱即用(如果您尝试上述代码,您将收到
    ProtocolViolationException
    )。谢天谢地,Microsoft提供了支持该功能的软件包—在构建
    HttpClient
    实例时,只需安装并使用它,而不是默认的:

      var handler = new WinHttpHandler();
      var client = new HttpClient(handler);
    
      <rest of code as above>
    
    var handler=new-WinHttpHandler();
    var client=新的HttpClient(处理程序);
    
    参考:


注意事项:

  • 带有主体的HTTP GET是一种有点非传统的构造,属于HTTP规范的灰色区域——最终结果是,许多较旧的软件要么根本无法处理这样的请求,要么会显式拒绝它,因为他们认为它的格式不正确。您需要非常确保您试图向其发送此类请求的端点确实支持它,否则最多只能返回HTTP错误代码;在最坏的情况下,尸体将被默默地丢弃。这可能会导致一些令人头痛的调试
  • 缓存代理服务器,尤其是较旧的代理服务器,可能只基于URL缓存GET请求,因为它们不希望存在主体。这可能会导致最近的请求被永久缓存(这将破坏您的软件),或者缓存的唯一请求是最近发出的请求(这将阻止缓存按预期工作)。同样,要弄清楚这一点可能非常痛苦

与上述答案相似,但代码较少

var请求=新的HttpRequestMessage
{
Method=HttpMethod.Get,
RequestUri=targetUri,
Content=新的StringContent(payload.payload),
};
var response=await client.sendaync(请求).ConfigureAwait(false);
var responseInfo=await response.Content.ReadAsStringAsync();

我不能使用.NET core,我不想安装
System.NET.Http.WinHttpHandler
,它有大量依赖项。 我通过使用反射解决了这个问题,欺骗
WebRequest
发送带有GET请求的body是合法的(根据最新的RFC)。我要做的是将HTTP动词“GET”的
ContentBodyNotAllowed
设置为false

var-request=WebRequest.Create(requestUri);
request.ContentType=“application/json”;
request.Method=“GET”;
var type=request.GetType();
var currentMethod=type.GetProperty(“currentMethod”,BindingFlags.NonPublic | BindingFlags.Instance).GetValue(请求);
var methodType=currentMethod.GetType();
methodType.GetField(“ContentBodyNotAllowed”,BindingFlags.NonPublic | BindingFlags.Instance).SetValue(currentMethod,false);
使用(var streamWriter=newstreamwriter(request.GetRequestStream()))
{
streamWriter.Write(“”);
}
var response=(HttpWebResponse)request.GetResponse();

但是,请注意,属性
ContentBodyNotAllowed
属于静态字段,因此当其值更改时,它对程序的其余部分仍然有效。这对我来说不是问题。

花了很多时间后,我无法使
HttpClient
WebRequest
在我的.Net核心项目中工作,显然服务器没有获取我的数据,它返回一个错误,说一些特定数据在我的请求中不可用。最后,在我的案例中只有
RestSharp
起作用(变量
myData
是以下代码中的
Dictionary
实例):


看看这个:所以基本上,即使你在理论上可以做到这一点,你也不应该这么做。你不能发送带有GET请求的正文,不能使用HttpClient或WebClient或其他任何东西。但是,即使您设法在较低级别上执行此操作,服务器也不会解析正文,因为它会将其视为GET请求。您不能这样做的原因是,根据定义,GET用于检索资源。不太可能有人需要那么多字符来检索资源。更可能的情况是,您打算提交数据,而不是重新查看数据,这正是POST的设计目的。能从哪里得到?id=&id=。。。。但它达到了4K限制,因此成为一篇文章也不好。@SelmanGenç错了,请看我的答案。这会引发ProtocolViolationException。@Alisson你在使用.NET Core吗?嗨,Ian!不,我使用的是.NET4.7,这是否只适用于.NETCore?非常感谢。在.NET4.7.2中,我不断收到
ContentType.Json
的错误,因此我不得不使用
“application/Json”
.life saver!这个解决方案就到此为止。工作完美!!一个漂亮的解决办法!请注意,由于这使用了反射,因此不能保证它会继续工作(例如,如果Microsoft重命名
ContentBodyNotAllowed
)。您是否有机会模拟它,以便在单元测试中涵盖它?@justinb我没有在单元测试中使用它
var client = new RestClient("some url");
var request = new RestRequest(Method.GET);
foreach (var d in myData)
    request.AddParameter(d.Key, d.Value);
var result = (await client.ExecuteAsync(request)).Content;