定位net461,HttpClient在GET请求有内容时抛出ex,但与netstandard2.0

时间:2018-04-30 13:26:19

标签: c# .net httpclient .net-standard

我需要使用第三方网络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的项目中使用,并抛出异常说“Cannot send a content-body with this verb-type

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

  1. 当我定位HttpClient.Send(request)时,net461怎么会失败,但是当我定位netstandard2.0时效果很好?
  2. 当我定位net461时,我有哪些选项?
  3. 更新

    重现的方法。

    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>
    

    Program.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

1 个答案:

答案 0 :(得分:3)

您没有提及在运行时运行它的.net版本(因为您可以定位网络标准但不能在网络标准上运行)。所以我假设当它运行时你在.NET Core上运行它,当它不运行时你就像你说的那样在完整的.NET 4.6.1上运行。

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

  

GET请求消息中的有效负载没有定义的语义;
  在GET请求上发送有效载荷主体可能会导致一些现有的   拒绝请求的实现。

因此,在GET中拥有主体(虽然几乎从不是一个好的实践)本身并不与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的名为KnownHttpVerb.Get的内部字段修改为false。使用风险自负。