Ориентируясь на net461, HttpClient генерирует ex, когда GET-запрос содержит контент, но работает с netstandard2.0 - PullRequest
0 голосов
/ 30 апреля 2018

Мне нужно использовать сторонний веб-интерфейс.
На этой конкретной конечной точке мне нужно сделать запрос GET с телом контента (json).

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>

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 голосов
/ 30 апреля 2018

Вы не упомянули, в какой версии .net вы запускаете ее, когда она работает (потому что вы можете настроить таргетинг на сетевой стандарт, но вы не можете запустить на сетевом стандарте). Поэтому я предполагаю, что когда он работает, вы запускаете его на .NET Core, а когда нет, вы работаете на полном .NET 4.6.1, как вы сказали.

Тогда это просто потому, что он был реализован по-разному в .NET Core и полной .NET Framework, как описано в этом выпуске . В этой реализации нет ничего плохого, потому что RFC говорит:

Полезная нагрузка в сообщении запроса GET не имеет определенной семантики;
отправка тела полезной нагрузки по запросу GET может привести к некоторому существующему
реализации, чтобы отклонить запрос.

Таким образом, наличие тела в GET (хотя это почти никогда не является хорошей практикой) само по себе не противоречит этому RFC. Одна реализация (более старая) была разработана, чтобы отклонить ее, но другая (более новая) была разработана, чтобы позволить это, в этом нет ничего более.

Что касается вашего второго вопроса "Какие у меня есть варианты, когда я нацеливаюсь на net461". Я не думаю, что есть простые обходные пути, потому что это поведение HttpWebRequest, а не HttpClient. И почти все, что делает веб-запросы, использует 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. Используйте на свой страх и риск.

...