ChatGPT解决这个技术问题 Extra ChatGPT

如何为 HttpClient 请求设置 Content-Type 标头?

我正在尝试按照我正在调用的 API 的要求设置 HttpClient 对象的 Content-Type 标头。

我尝试如下设置 Content-Type

using (var httpClient = new HttpClient())
{
    httpClient.BaseAddress = new Uri("http://example.com/");
    httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
    httpClient.DefaultRequestHeaders.Add("Content-Type", "application/json");
    // ...
}

它允许我添加 Accept 标头,但是当我尝试添加 Content-Type 时,它会引发以下异常:

误用的标头名称。确保请求标头与 HttpRequestMessage 一起使用,响应标头与 HttpResponseMessage 一起使用,内容标头与 HttpContent 对象一起使用。

如何在 HttpClient 请求中设置 Content-Type 标头?

您可以按照 .NET Core 中的 HttpWebRequest 的操作方式(它在内部使用 HttpClient),请参阅github.com/dotnet/corefx/blob/master/src/System.Net.Requests/…“SendRequest”方法

L
Liam

内容类型是内容的标头,而不是请求的标头,这就是失败的原因。 AddWithoutValidation as suggested by Robert Levy 可能有效,但您也可以在创建请求内容本身时设置内容类型(请注意,代码片段在两个地方添加 application/json - 用于 Accept 和 Content-Type 标头):

HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://example.com/");
client.DefaultRequestHeaders
      .Accept
      .Add(new MediaTypeWithQualityHeaderValue("application/json"));//ACCEPT header

HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "relativeAddress");
request.Content = new StringContent("{\"name\":\"John Doe\",\"age\":33}",
                                    Encoding.UTF8, 
                                    "application/json");//CONTENT-TYPE header

client.SendAsync(request)
      .ContinueWith(responseTask =>
      {
          Console.WriteLine("Response: {0}", responseTask.Result);
      });

或者,Response.Content.Headers 将在大部分时间工作。
@AshishJain 我见过的涉及 ASP.Net Web API 的 Response.Content.Headers 的大多数 SO 答案也不起作用,但如果需要,您可以使用 HttpContext.Current.Response.ContentType 轻松设置它。
@jerhewet 我以以下方式使用,这对我有用。 var content = new StringContent(data, Encoding.UTF8, "application/json");
Content-Type 是带有有效负载的 HTTP 消息的属性;它与请求与响应无关。
有趣的。我尝试使用三个参数创建一个新的 StringContent ,但它不起作用。然后我手动: request.Content.Headers.Remove("Content-Type") 然后: request.Content.Headers.Add("Content-Type", "application/query+json") 并且它起作用了。奇怪的。
a
archgl

对于那些没有看到约翰对卡洛斯解决方案发表评论的人......

req.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");

下载pdf文件有所不同。它试图从手机下载 HTML。转换扩展名后,文件通常被编码。
最后我不得不抛出 .ToString() ,但是是的,这适用于 WCF 服务实现。
我最终会弄清楚“req”是什么对象类型......通过反复试验......但展示这一点会很棒。谢谢您的考虑。
众所周知,如果尝试设置字符集,使用 MediaTypeHeaderValue 将返回错误,如下所示; response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/xml; charset=utf-8");
Johns 对 Carlo 的解决方案的评论说 Response.Content.Headers,但您使用的是 req.Content.Headers 吗?即请求与响应?
W
WestDiscGolf

如果您不介意小型库依赖项,Flurl.Http [披露:我是作者] 让这个超级简单。它的 PostJsonAsync 方法负责序列化内容和设置 content-type 标头,并且 ReceiveJson 反序列化响应。如果需要 accept 标头,您需要自己设置,但 Flurl 也提供了一种非常简洁的方法:

using Flurl.Http;

var result = await "http://example.com/"
    .WithHeader("Accept", "application/json")
    .PostJsonAsync(new { ... })
    .ReceiveJson<TResult>();

Flurl 在后台使用 HttpClient 和 Json.NET,它是一个 PCL,因此可以在各种平台上工作。

PM> Install-Package Flurl.Http

如果内容是 application/x-www-form-urlencoded,如何发送?
S
SharpCoder

尝试使用 TryAddWithoutValidation

  var client = new HttpClient();
  client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json; charset=utf-8");

不工作给了我一个“误用的标题名称。确保请求标头与 HttpRequestMessage 一起使用,响应标头与 HttpResponseMessage 一起使用,内容标头与 HttpContent 对象一起使用。
你们这些报告“工作”或“不工作”的人,现在 HttpClient 是一个非常模棱两可的对象。请报告它来自的全名(空格)和 .dll 程序集。
dotnet core 2.2 确认了 Misused header name 错误。我不得不使用@carlosfigueira 的回答stackoverflow.com/a/10679340/2084315
e
erdomke

.Net 试图强制您遵守某些标准,即只能在具有内容的请求上指定 Content-Type 标头(例如 POSTPUT 等)。因此,正如其他人所指出的,设置 Content-Type 标头的首选方法是通过 HttpContent.Headers.ContentType 属性。

话虽如此,某些 API(例如 LiquidFiles Api,截至 2016 年 12 月 19 日)需要为 GET 请求设置 Content-Type 标头。 .Net 不允许在请求本身上设置此标头——即使使用 TryAddWithoutValidation。此外,您不能为请求指定 Content——即使它的长度为零。我似乎可以解决这个问题的唯一方法是诉诸反思。代码(以防其他人需要它)是

var field = typeof(System.Net.Http.Headers.HttpRequestHeaders)
    .GetField("invalidHeaders", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static) 
  ?? typeof(System.Net.Http.Headers.HttpRequestHeaders) 
    .GetField("s_invalidHeaders", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
if (field != null)
{
  var invalidFields = (HashSet<string>)field.GetValue(null);
  invalidFields.Remove("Content-Type");
}
_client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "text/xml");

编辑:

如评论中所述,该字段在不同版本的 dll 中具有不同的名称。在 source code on GitHub 中,该字段当前名为 s_invalidHeaders。根据@David Thompson 的建议,该示例已被修改以解决此问题。


不适用于 .Net Framework 4.0 版、System.Net.Http 2.2.29.0 版,但可以使用 2.0.0.0
该字段现在是 s_invalidHeaders,因此使用以下内容可确保兼容性: var field = typeof(System.Net.Http.Headers.HttpRequestHeaders) .GetField("invalidHeaders", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static) ?? typeof(System.Net.Http.Headers.HttpRequestHeaders) .GetField("s_invalidHeaders", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
我对这段代码如何导致您描述的灾难性错误感到困惑。您能否提供有关您的用例和您收到的错误的更多详细信息?
哇。更令人惊叹的是,Asp.net WebApi GET 方法需要明确指定 Content-Type =(
霍莉莫莉,我不敢相信我不得不求助于这个。从什么时候开始 .NET 框架开发人员需要牵手我可以添加到 Http 标头部分的内容?可恶。
H
Hassan Faghihi

对于那些对字符集感到困扰的人

我有一个非常特殊的情况,服务提供者不接受字符集,他们拒绝更改子结构以允许它......不幸的是,HttpClient 正在通过 StringContent 自动设置标题,无论您传递 null 还是 Encoding.UTF8,它总是会设置字符集...

今天我处于改变子系统的边缘;从 HttpClient 转移到其他任何东西,我想到了一些东西......为什么不使用反射来清空“字符集”? ...在我尝试之前,我想到了一种方法,“也许我可以在初始化后更改它”,并且有效。

以下是如何在没有“; charset=utf-8”的情况下设置确切的“application/json”标头。

var jsonRequest = JsonSerializeObject(req, options); // Custom function that parse object to string
var stringContent = new StringContent(jsonRequest, Encoding.UTF8, "application/json");
stringContent.Headers.ContentType.CharSet = null;
return stringContent;

注意:后面的 null 值不起作用,并附加“; charset=utf-8”

return new StringContent(jsonRequest, null, "application/json");

编辑

@DesertFoxAZ 建议也可以使用以下代码并且可以正常工作。 (我自己没有测试,如果它的工作率和评论他的功劳)

stringContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");

stringContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");也有效
J
Jay

有关 .NET Core 的一些额外信息(在阅读 erdomke 关于设置私有字段以在没有内容的请求上提供内容类型的帖子后)...

调试我的代码后,我看不到要通过反射设置的私有字段 - 所以我想我会尝试重新创建问题。

我使用 .Net 4.6 尝试了以下代码:

HttpRequestMessage httpRequest = new HttpRequestMessage(HttpMethod.Get, @"myUrl");
httpRequest.Content = new StringContent(string.Empty, Encoding.UTF8, "application/json");

HttpClient client = new HttpClient();
Task<HttpResponseMessage> response =  client.SendAsync(httpRequest);  //I know I should have used async/await here!
var result = response.Result;

而且,正如预期的那样,我得到了一个内容为 "Cannot send a content-body with this verb-type." 的聚合异常

但是,如果我对 .NET Core (1.1) 做同样的事情 - 我不会遇到异常。我的服务器应用程序很高兴地回答了我的请求,并且获取了内容类型。

我对此感到惊喜,我希望它对某人有所帮助!


谢谢,杰 - 不使用核心,将使用 erdomke 的答案。我很高兴知道所有合理的途径都已尝试:)。
不工作.net 4({“无法发送具有此动词类型的内容主体。”})
@TarekEl-Mallah 是的-请阅读我回答中的评论。我这篇文章的重点是说明它在 .NET 4 中不起作用,但在 .NET 核心中确实起作用(它们不是一回事)。您必须查看 erdomeke 对 OP 问题的回答才能破解它以在 .NET 4 中工作。
K
Keith Pinson

调用 AddWithoutValidation 而不是 Add(请参阅 this MSDN link)。

或者,我猜您正在使用的 API 实际上只需要 POST 或 PUT 请求(不是普通的 GET 请求)。在这种情况下,当您调用 HttpClient.PostAsync 并传入 HttpContent 时,在该 HttpContent 对象的 Headers 属性上设置它。


不工作给了我一个“误用的标题名称。确保请求标头与 HttpRequestMessage 一起使用,响应标头与 HttpResponseMessage 一起使用,内容标头与 HttpContent 对象一起使用。
AddWithoutValidation 不存在
R
Roland

诀窍是您可以设置各种标题,例如:

HttpRequestMessage request = new HttpRequestMessage();
request.Headers.Add("Accept-Language", "en");

但没有任何标题。例如:

request.Headers.Add("Content-Type", "application/json");//wrong

将引发运行时异常 Misused header name。这似乎可行:

request.Headers.Add(
   HttpRequestHeader.ContentType.ToString(), //useless
   "application/json"
);

但这给出了一个名为 ContentType 的无用标题,没有连字符。标题名称不区分大小写,但对连字符非常敏感。

在将正文添加到 http 请求的 Content 部分时,您必须声明正文的编码和类型:

string Body = "...";
request.Content = new StringContent(Body, Encoding.UTF8, "application/json");

只有这样,适用的 http 标头才会自动添加到请求中:

Content-Type: application/json; charset=utf-8

在没有代理服务器的机器上,使用 Fiddler 很难找到这一点。 Visual Studio 曾经有一个网络工具,您可以在其中检查所有标头,但仅在 2015 版中,而不是在较新的版本 2017 或 2022 中。如果您使用调试器检查 request.Headers,您将找不到由 { 自动添加的标头2}。


a
art24war
var content = new JsonContent();
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
content.Headers.ContentType.Parameters.Add(new NameValueHeaderValue("charset", "utf-8"));
content.Headers.ContentType.Parameters.Add(new NameValueHeaderValue("IEEE754Compatible", "true"));

这就是你所需要的。

使用 Newtonsoft.Json,如果您需要内容作为 json 字符串。

public class JsonContent : HttpContent
   {
    private readonly MemoryStream _stream = new MemoryStream();
    ~JsonContent()
    {
        _stream.Dispose();
    }

    public JsonContent(object value)
    {
        Headers.ContentType = new MediaTypeHeaderValue("application/json");
        using (var contexStream = new MemoryStream())
        using (var jw = new JsonTextWriter(new StreamWriter(contexStream)) { Formatting = Formatting.Indented })
        {
            var serializer = new JsonSerializer();
            serializer.Serialize(jw, value);
            jw.Flush();
            contexStream.Position = 0;
            contexStream.WriteTo(_stream);
        }
        _stream.Position = 0;

    }

    private JsonContent(string content)
    {
        Headers.ContentType = new MediaTypeHeaderValue("application/json");
        using (var contexStream = new MemoryStream())
        using (var sw = new StreamWriter(contexStream))
        {
            sw.Write(content);
            sw.Flush();
            contexStream.Position = 0;
            contexStream.WriteTo(_stream);
        }
        _stream.Position = 0;
    }

    protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
    {
        return _stream.CopyToAsync(stream);
    }

    protected override bool TryComputeLength(out long length)
    {
        length = _stream.Length;
        return true;
    }

    public static HttpContent FromFile(string filepath)
    {
        var content = File.ReadAllText(filepath);
        return new JsonContent(content);
    }
    public string ToJsonString()
    {
        return Encoding.ASCII.GetString(_stream.GetBuffer(), 0, _stream.GetBuffer().Length).Trim();
    }
}

你能简单解释一下它的作用吗?
第一行以 CS0144 失败:“无法创建抽象类或接口 'HttpContent' 的实例”
然后HttpMessageHandler handler = new WebRequestHandler(); HttpResponseMessage result; using (var client = (new HttpClient(handler, true))) { result = client.PostAsync(fullUri, content).Result; }
K
Kumaran

你可以用这个它会工作!

HttpRequestMessage msg = new HttpRequestMessage(HttpMethod.Get,"URL");
msg.Content = new StringContent(string.Empty, Encoding.UTF8, "application/json");

HttpResponseMessage response = await _httpClient.SendAsync(msg);
response.EnsureSuccessStatusCode();

string json = await response.Content.ReadAsStringAsync();

这仅适用于 .NET Core,不适用于 .NET Framework。
y
yoel halb

似乎微软试图强迫开发人员遵循他们的标准,甚至没有提供任何选项或设置来做其他事情,这真的很可惜,特别是考虑到这是一个客户端,我们受服务器端要求的支配,特别是考虑到Microsoft 服务器端框架本身需要它!

因此,基本上微软在连接到强迫我们养成不良习惯的服务器技术时,试图强迫我们养成良好的习惯……

如果 Microsoft 的任何人正在阅读此内容,请修复它...

无论哪种方式,对于需要 Get 等内容类型标头的任何人,在较旧的 .Net 版本中,都可以在 https://stackoverflow.com/a/41231353/640195 处使用@erdomke 的答案,不幸的是,这在较新的 .Net 核心版本中不再有效。

以下代码已经过测试,可以与 .Net core 3.1 一起使用,从 GitHub 上的源代码来看,它似乎也可以与较新的 .Net 版本一起使用。

private void FixContentTypeHeaders()
{
    var assembly = typeof(System.Net.Http.Headers.HttpRequestHeaders).Assembly;
    var assemblyTypes = assembly.GetTypes();

    var knownHeaderType = assemblyTypes.FirstOrDefault(n => n.Name == "KnownHeader");
    var headerTypeField = knownHeaderType?
                .GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
                .FirstOrDefault(n => n.Name.Contains("HeaderType"));
    if (headerTypeField is null) return;

    var headerTypeFieldType = headerTypeField.FieldType;            
    var newValue = Enum.Parse(headerTypeFieldType, "All");

    var knownHeadersType = assemblyTypes.FirstOrDefault(n => n.Name == "KnownHeaders");
    var contentTypeObj = knownHeadersType.GetFields().FirstOrDefault(n => n.Name == "ContentType").GetValue(null);

    if (contentTypeObj is null) return;

    headerTypeField.SetValue(contentTypeObj, newValue);
}

为什么所有这些反思都是必要的?决定 Content-Type 的是内容对象,您可以在 GET 请求以及 POST、PUT 等上添加内容对象。如果您没有内容对象(正文),则 Content-Type 是一个不相关的标头。
@John有一些框架,包括微软自己的框架,即使是Get也需要标头,即使没有内容,也不要问我为什么......以及为什么他们的客户端在他们的服务器期望它时删除它......
我猜微软团队之间有一场战斗,我们是中间的猴子......
除非 Content-Length 标头出现问题,否则仅创建一个派生的 HttpContent 对象以允许您指定标头而不是进行所有这些反射不是更好吗?
Z
Ziba Leah

好的,它不是 HTTPClient,但如果你可以使用它,WebClient 非常简单:

using (var client = new System.Net.WebClient())
 {
    client.Headers.Add("Accept", "application/json");
    client.Headers.Add("Content-Type", "application/json; charset=utf-8");
    client.DownloadString(...);
 }

u
user890332

你需要这样做:

    HttpContent httpContent = new StringContent(@"{ the json string }");
    httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));                
    HttpResponseMessage message = client.PostAsync(@"{url}", httpContent).Result;

F
Felipe Augusto

尝试使用 HttpClientFactory

services.AddSingleton<WebRequestXXX>()
        .AddHttpClient<WebRequestXXX>("ClientX", config =>
        {
           config.BaseAddress = new System.Uri("https://jsonplaceholder.typicode.com");
           config.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
           config.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json; charset=utf-8");
        });

=======================

public class WebRequestXXXX
{
    private readonly IHttpClientFactory _httpClientFactory;

    public WebRequestXXXX(IHttpClientFactory httpClientFactory)
    {
        _httpClientFactory = httpClientFactory;
    }

    public List<Posts> GetAllPosts()
    {
        using (var _client = _httpClientFactory.CreateClient("ClientX"))
        {
            var response = _client.GetAsync("/posts").Result;

            if (response.IsSuccessStatusCode)
            {
                var itemString = response.Content.ReadAsStringAsync().Result;
                var itemJson = System.Text.Json.JsonSerializer.Deserialize<List<Posts>>(itemString, 
                    new System.Text.Json.JsonSerializerOptions 
                    {
                        PropertyNameCaseInsensitive = true
                    });

                return itemJson;
            }
            else
            {
                return new List<Posts>();
            }
        }
    }
}

S
Sérgio Damasceno

我得到了 RestSharp 的答案:

        private async Task<string> GetAccessTokenAsync()
        {
            var client = new RestClient(_baseURL);

            var request = new RestRequest("auth/v1/login", Method.POST, DataFormat.Json);

            request.AddHeader("Content-Type", "application/json");
            request.AddHeader("x-api-key", _apiKey);
            request.AddHeader("Accept-Language", "br");
            request.AddHeader("x-client-tenant", "1");
        
            ...
        }

它对我有用。


z
zawar

对于那些想要专门将 Content-Type 设置为 Json 的人,您可以使用扩展方法 PostAsJsonAsync。

using System.Net.Http.Json; //this is needed for PostAsJsonAsync to work
//....
HttpClient client = new HttpClient();
HttpResponseMessage response = await
    client.PostAsJsonAsync("http://example.com/" + "relativeAddress",
                new
                {
                    name = "John Doe",
                    age = 33
                });
//Do what you need to do with your response

这里的优点是代码更简洁,您可以避免字符串化的 json。更多详细信息,请访问:https://docs.microsoft.com/en-us/previous-versions/aspnet/hh944339(v=vs.118)


我遇到的问题是 PostAsJsonAsync 没有设置 Content-type 标头!
l
leocrimson

我发现通过以下方式最简单易懂:

async Task SendPostRequest()
{
    HttpClient client = new HttpClient();
    var requestContent = new StringContent(<content>);
    requestContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
    var response = await client.PostAsync(<url>, requestContent);
    var responseString = await response.Content.ReadAsStringAsync();
}
...

SendPostRequest().Wait();

H
Hugo

我最终遇到了类似的问题。所以我发现Software PostMan可以在点击左上角的“代码”按钮时生成代码。从中我们可以看到“幕后”发生的事情,并且 HTTP 调用是用多种代码语言生成的; curl 命令,C# RestShat,java,nodeJs,...

这对我有很大帮助,我最终没有使用.Net base HttpClient,而是使用了 RestSharp nuget 包。

希望可以帮助别人!


s
savie

api返回

“不支持的媒体类型”,“状态”:415

将 ContentType 添加到 jsonstring 起到了神奇的作用,这是我的脚本到今天为止 100% 工作

    using (var client = new HttpClient())
            {
                   var endpoint = "api/endpoint;
                    var userName = "xxxxxxxxxx";
                    var passwd = "xxxxxxxxxx";
                    var content = new StringContent(jsonString, Encoding.UTF8, "application/json");

                    var authToken = Encoding.ASCII.GetBytes($"{userName}:{passwd}");

                client.BaseAddress = new Uri("https://example.com/");

                client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(authToken));
            
                HttpResponseMessage response = await client.PostAsync(endpoint, content);

                if (response.IsSuccessStatusCode)
                {
                    // Get the URI of the created resource.
                    Uri returnUrl = response.Headers.Location;
                    Console.WriteLine(returnUrl);
                }
                string responseBody = await response.Content.ReadAsStringAsync();
                return responseBody;
        }

P
Pang

stringContent.Headers.ContentType = new MediaTypeHeaderValue(contentType);

而且🎉是的! 🎉 ...这解决了 ATS REST API 的问题:SharedKey 现在可以工作了! 😄 👍 🍻

来源:https://github.com/dotnet/runtime/issues/17036#issuecomment-212046628


请编辑您的问题,尝试解释问题和您的解决方案(没有 imojis)