首页 > 解决方案 > 以 net461 为目标,当 GET 请求有内容时,HttpClient 会抛出 ex,但适用于 netstandard2.0

问题描述

我需要使用 3rd 方 web api。
在这个特定的端点上,我需要使用内容主体(json)发出 GET 请求。

var jsonPayload = JsonConvert.SerializeObject(myObject);

var request = new HttpRequestMessage(HttpMethod.Get, endpoint)
{
    Content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
};

var client = new HttpClient();
var response = await client.SendAsync(request); //<-- explodes with net461,
//but works with netstandard2.0
var responseContent = await response.Content.ReadAsStringAsync();

我将该代码定位到netstandard2.0并且它有效。
但是我现在需要在我的目标项目中使用net461它并抛出异常说“无法发送具有此动词类型的内容主体

我知道将内容设置为 GET 请求是不常见的,但该 api 超出了我的范围。

  1. 为什么HttpClient.Send(request)我瞄准时失败net461,但我瞄准时效果很好netstandard2.0
  2. 当我瞄准时我有什么选择net461

更新

一种繁殖方式。

ConsoleApp.csproj

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.0</TargetFramework> <!-- toggle this to net461 -->
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
  </ItemGroup>

  <ItemGroup>
    <Reference Include="System.Net.Http" />
  </ItemGroup>

</Project>

程序.cs

using Newtonsoft.Json;
using System;
using System.Net.Http;
using System.Text;

namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            var jsonPayload = JsonConvert.SerializeObject(new { });
            var request = new HttpRequestMessage(HttpMethod.Get, "http://www.stackoverflow.com")
            {
                Content = new StringContent(jsonPayload, Encoding.UTF8, "application/json")
            };
            var client = new HttpClient();
            var response = client.SendAsync(request).Result;
            var responseContent = response.Content.ReadAsStringAsync().Result;
            Console.WriteLine(responseContent);
        }
    }
}

dotnet --version节目2.1.104

标签: c#.nethttpclient.net-standard

解决方案


你没有提到你在哪个 .net 版本上运行它(因为你可以针对网络标准但你不能在网络标准上运行)。所以我假设当它工作时,你在 .NET Core 上运行它,而当它不工作时,你在完整的 .NET 4.6.1 上运行,如你所说。

那只是因为它在 .NET Core 和完整的 .NET Framework 中的实现方式不同,如本期所述。这两种实现都没有错,因为 RFC 说:

GET 请求消息中的有效负载没有定义的语义;
在 GET 请求上发送有效负载正文可能会导致某些现有
实现拒绝该请求。

因此,在 GET 中包含 body(虽然几乎从来都不是一个好的做法)本身并不与 RFC 相矛盾。一种实现(较旧的)旨在拒绝它,但另一种(较新的)旨在允许它,仅此而已。

至于你的第二个问题“当我瞄准 net461 时我有什么选择”。我认为没有简单的解决方法,因为这是 的行为HttpWebRequest,而不是具体的HttpClient. 几乎所有发出 Web 请求的东西都在HttpWebRequest内部使用。

虽然有一些使用反射的丑陋黑客。您的示例可以这样修改(仅在完整的 .NET 上运行时有效):

static void Main(string[] args) {     
    // UGLY HACK!       
    var verbType = typeof(HttpWebRequest).Assembly.GetType("System.Net.KnownHttpVerb");
    var getMethodField = verbType.GetField("Get", BindingFlags.Static | BindingFlags.NonPublic);
    var getMethod = getMethodField.GetValue(null);
    verbType.GetField("ContentBodyNotAllowed", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(getMethod, false);
    var jsonPayload = JsonConvert.SerializeObject(new { });
    var request = new HttpRequestMessage(HttpMethod.Get, "http://www.stackoverflow.com")
    {
        Content = new StringContent(jsonPayload, Encoding.UTF8, "application/json")
    };
    var client = new HttpClient();
    var response = client.SendAsync(request).Result;
    var responseContent = response.Content.ReadAsStringAsync().Result;
    Console.WriteLine(responseContent);            
}

ContentBodyNotAllowed基本上,我们使用反射将名为of 的内部字段修改KnownHttpVerb.Get为 false。使用风险自负。


推荐阅读