提问人:mynameiscoffey 提问时间:5/21/2012 最后编辑:Alexander Abakumovmynameiscoffey 更新时间:11/17/2023 访问量:1375575
如何为HttpClient请求设置Content-Type标头?
How to set the Content-Type header for an HttpClient request?
问:
我正在尝试根据我调用的 API 的要求设置对象的标头。Content-Type
HttpClient
我尝试设置如下: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
如何在请求中设置标头?Content-Type
HttpClient
答:
代替 Call(请参阅此 MSDN 链接)。AddWithoutValidation
Add
或者,我猜您使用的 API 实际上只需要 POST 或 PUT 请求(而不是普通的 GET 请求)。在这种情况下,当您调用并传入 时,请在该对象的属性上设置此项。HttpClient.PostAsync
HttpContent
Headers
HttpContent
评论
内容类型是内容的标头,而不是请求的标头,这就是失败的原因。Robert Levy 建议的 AddWithoutValidation
可能有效,但您也可以在创建请求内容本身时设置内容类型(请注意,代码片段在两个位置添加 - 用于 Accept 和 Content-Type 标头):application/json
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
Response.Content.Headers
HttpContext.Current.Response.ContentType
req.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
评论
response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/xml; charset=utf-8");
如果你不介意一个小的库依赖,Flurl.Http [披露:我是作者]使这变得超级简单。它的方法负责序列化内容和设置标头,并反序列化响应。如果需要标头,则需要自己设置,但 Flurl 也提供了一种非常干净的方法:PostJsonAsync
content-type
ReceiveJson
accept
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
评论
尝试使用 TryAddWithoutValidation
var client = new HttpClient();
client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json; charset=utf-8");
评论
Misused header name
.Net 试图强制您遵守某些标准,即只能在包含内容(例如 、 等)的请求上指定标头。因此,正如其他人所指出的,设置标头的首选方法是通过 HttpContent.Headers.ContentType
属性。Content-Type
POST
PUT
Content-Type
话虽如此,某些 API(例如截至 2016 年 12 月 19 日的 LiquidFiles Api)需要设置请求的标头。.Net 不允许在请求本身上设置此标头 -- 即使使用 .此外,您不能为请求指定 a,即使它的长度为零。我似乎唯一能解决这个问题的方法就是诉诸反思。代码(以防其他人需要它)是Content-Type
GET
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 中具有不同的名称。在 GitHub 上的源代码中,该字段当前名为 .根据 @David Thompson 的建议,该示例已被修改以说明这一点。s_invalidHeaders
评论
有关 .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;
而且,正如预期的那样,我得到了内容的聚合异常
无法发送具有此谓词类型的内容正文。
但是,如果我对 .NET Core (1.1) 执行相同的操作,则不会出现异常。我的服务器应用程序非常高兴地回答了我的请求,并且选择了内容类型。
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();
}
}
评论
HttpMessageHandler handler = new WebRequestHandler(); HttpResponseMessage result; using (var client = (new HttpClient(handler, true))) { result = client.PostAsync(fullUri, content).Result; }
好的,它不是 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(...);
}
我发现它最简单易懂的方式如下:
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();
你可以使用它,这将是工作!
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();
评论
你需要这样做:
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;
对于那些烦恼的人charset
我有一个非常特殊的情况,服务提供商不接受字符集,他们拒绝更改子结构以允许它...... 不幸的是,HttpClient 通过 StringContent 自动设置标头,无论您是传递 null 还是 Encoding.UTF8,它都将始终设置字符集......
今天我正处于更改子系统的边缘;从 HttpClient 移动到其他任何东西,我想到了一些东西......,为什么不使用反射来清空“字符集”呢?... 在我尝试之前,我想到了一种方法,“也许我可以在初始化后更改它”,这奏效了。
以下是如何设置确切的“application/json”标头,而不使用“;字符集=UTF-8”。
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;
注意:以下值将不起作用,并附加“;字符集=utf-8”null
return new StringContent(jsonRequest, null, "application/json");
编辑
@DesertFoxAZ建议也可以使用以下代码并且工作正常。(我自己没有测试过,如果它有效,请在评论中称赞他)
stringContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
评论
尝试使用 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>();
}
}
}
}
评论
对于需要 Get 等内容类型标头的任何人,虽然在较旧的 .NET 版本中可以使用 https://stackoverflow.com/a/41231353@erdomke 的答案,但不幸的是,这在较新的 .NET Core 版本中不再有效。
以下代码已经过测试,适用于 .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);
}
评论
HttpContent
我用 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");
...
}
它对我有用。
评论
对于那些想要将 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://learn.microsoft.com/en-us/previous-versions/aspnet/hh944339(v=vs.118)
评论
Content-type
诀窍是您可以设置各种标头,例如:
HttpRequestMessage request = new HttpRequestMessage();
request.Headers.Add("Accept-Language", "en"); //works OK
但不是任何标头。例如:
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
StringContent()
评论
返回的 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;
}
就我的方案而言,第三方 API 正在创建 ,因此我无法使用票数最高的答案来解决问题。而且我不喜欢乱用反思的想法,所以其他答案也不起作用。HttpRequestMessage
取而代之的是,我从这个新类扩展并使用了这个新类作为参数。 包含可以重写的方法,以便在发送对象之前对对象进行更改。如果无法访问 Android Xamarin 库,则可以使用 .AndroidMessageHandler
HttpClient
AndroidMessageHandler
SendAsync
HttpRequestMessage
HttpMessageHandler
public class XamarinHttpMessageHandler : global::Xamarin.Android.Net.AndroidMessageHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
// Here I make check that I'm only modifying a specific request
// and not all of them.
if (request.RequestUri != null && request.RequestUri.AbsolutePath.EndsWith("download") && request.Content != null)
{
request.Content.Headers.Add("Content-Type", "text/plain");
}
return base.SendAsync(request, cancellationToken);
}
}
然后使用:
var client = new HttpClient(new XamarinHttpMessageHandler());
因此,如果你尝试执行 OData 请求,请像这篇 Microsoft 文章一样演示你应该在哪里有一个 Content-Type 标头,如下所示:/$batch
内容类型:多部分/混合;边界=batch_d3bcb804-ee77-4921-9a45-761f98d32029
string headerValue = "multipart/mixed;boundary=batch_d3bcb804-ee77-4921-9a45-761f98d32029";
//You need to set it like thus:
request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse(headerValue);
同样,您需要的魔力是:MediaTypeHeaderValue.Parse(...)
像这样设置内容类型(我的情况是设置“application/vnd.api+json”)对我有用:
var requestBody = new StringContent("{"data": "1"}"), new MediaTypeHeaderValue("application/vnd.api+json"));
var response = await _httpClient.PostAsync(apiUrl, requestBody);
默认情况下,在 GET 方法中禁止使用 Content-Type 标头,至少在 -NET Framework 实现中是这样。 我使用继承自 HttpClient 类使用的自定义处理程序来解决问题。我重写了 SendAsync 方法,该方法消除了禁止的标头(否则,当您尝试将标头添加到请求时,您将出现异常),并且当请求具有 GET 方法时,我包含标头:
public class GetRequestHandler : HttpClientHandler {
public GetRequestHandler(): base() {
}
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken) {
FieldInfo invalidHeadersField = typeof(HttpHeaders).GetField("invalidHeaders", BindingFlags.NonPublic | BindingFlags.Instance) ?? typeof(HttpHeaders).GetField("_invalidHeaders", BindingFlags.NonPublic | BindingFlags.Instance);
HashSet<string> invalidHeaders = (HashSet<string>)invalidHeadersField.GetValue(request.Headers);
invalidHeaders.Remove("Content-Type");
if(request.Method == HttpMethod.Get) {
request.Headers.Remove("Content-Type");
request.Headers.Add("Content-Type", "application/json");
}
return base.SendAsync(request, cancellationToken);
}
}
然后,在 HttpClient 构造函数中,我使用此自定义处理程序:
HttpClient client = new HttpClient(new GetRequestHandler());
这样做,现在效果很好:
var response = await client.GetAsync(url);
评论