我正在尝试按照我正在调用的 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
标头?
内容类型是内容的标头,而不是请求的标头,这就是失败的原因。 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);
});
对于那些没有看到约翰对卡洛斯解决方案发表评论的人......
req.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/xml; charset=utf-8");
如果您不介意小型库依赖项,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
尝试使用 TryAddWithoutValidation
var client = new HttpClient();
client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json; charset=utf-8");
Misused header name
错误。我不得不使用@carlosfigueira 的回答stackoverflow.com/a/10679340/2084315。
.Net 试图强制您遵守某些标准,即只能在具有内容的请求上指定 Content-Type
标头(例如 POST
、PUT
等)。因此,正如其他人所指出的,设置 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 的建议,该示例已被修改以解决此问题。
对于那些对字符集感到困扰的人
我有一个非常特殊的情况,服务提供者不接受字符集,他们拒绝更改子结构以允许它......不幸的是,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");
有关 .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) 做同样的事情 - 我不会遇到异常。我的服务器应用程序很高兴地回答了我的请求,并且获取了内容类型。
我对此感到惊喜,我希望它对某人有所帮助!
调用 AddWithoutValidation
而不是 Add
(请参阅 this MSDN link)。
或者,我猜您正在使用的 API 实际上只需要 POST 或 PUT 请求(不是普通的 GET 请求)。在这种情况下,当您调用 HttpClient.PostAsync
并传入 HttpContent
时,在该 HttpContent
对象的 Headers
属性上设置它。
诀窍是您可以设置各种标题,例如:
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}。
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; }
你可以用这个它会工作!
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();
似乎微软试图强迫开发人员遵循他们的标准,甚至没有提供任何选项或设置来做其他事情,这真的很可惜,特别是考虑到这是一个客户端,我们受服务器端要求的支配,特别是考虑到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);
}
HttpContent
对象以允许您指定标头而不是进行所有这些反射不是更好吗?
好的,它不是 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(...);
}
你需要这样做:
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;
尝试使用 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>();
}
}
}
}
我得到了 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://docs.microsoft.com/en-us/previous-versions/aspnet/hh944339(v=vs.118)
Content-type
标头!
我发现通过以下方式最简单易懂:
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();
我最终遇到了类似的问题。所以我发现Software PostMan可以在点击左上角的“代码”按钮时生成代码。从中我们可以看到“幕后”发生的事情,并且 HTTP 调用是用多种代码语言生成的; curl 命令,C# RestShat,java,nodeJs,...
这对我有很大帮助,我最终没有使用.Net base HttpClient,而是使用了 RestSharp nuget 包。
希望可以帮助别人!
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;
}
stringContent.Headers.ContentType = new MediaTypeHeaderValue(contentType);
而且🎉是的! 🎉 ...这解决了 ATS REST API 的问题:SharedKey 现在可以工作了! 😄 👍 🍻
来源:https://github.com/dotnet/runtime/issues/17036#issuecomment-212046628
不定期副业成功案例分享
Response.Content.Headers
将在大部分时间工作。Response.Content.Headers
的大多数 SO 答案也不起作用,但如果需要,您可以使用HttpContext.Current.Response.ContentType
轻松设置它。