如何更改 FLURL 客户端的 HTTP 请求内容类型?
How to change the HTTP Request Content Type for FLURL Client?
我正在使用 flurl 提交 HTTP 请求,这非常有用。现在我需要将某些请求的“Content-Type” header 更改为 "application/json;odata=verbose"
public async Task<Job> AddJob()
{
var flurlClient = GetBaseUrlForGetOperations("Jobs").WithHeader("Content-Type", "application/json;odata=verbose");
return await flurlClient.PostJsonAsync(new
{
//Some parameters here which are not the problem since tested with Postman
}).ReceiveJson<Job>();
}
private IFlurlClient GetBaseUrlForOperations(string resource)
{
var url = _azureApiUrl
.AppendPathSegment("api")
.AppendPathSegment(resource)
.WithOAuthBearerToken(AzureAuthentication.AccessToken)
.WithHeader("x-ms-version", "2.11")
.WithHeader("Accept", "application/json");
return url;
}
你可以看到我是如何尝试添加上面的 header (.WithHeader("Content-Type", "application/json;odata=verbose")
)
不幸的是,这给了我以下错误:
"InvalidOperationException: Misused header name. Make sure request
headers are used with HttpRequestMessage, response headers with
HttpResponseMessage, and content headers with HttpContent objects."
我也试过flurl的"ConfigureHttpClient"方法但是找不到how/where来设置内容类型header.
我不是 OData 专家,我不知道 API 你叫什么(SharePoint?),但根据我见过的大多数例子,你通常想要做的是要求服务器在响应中发送详细的 OData,而不是声明您在请求中发送它。换句话说,您想在 Accept header 上设置 ;odata=verbose
位,而不是 Content-Type . application/json
对 Content-Type 应该足够好了,Flurl 会自动为您设置,所以只需尝试这个更改,看看它是否有效:
.WithHeader("Accept", "application/json;odata=verbose");
评论和我发现的另一个 post(当我再次找到它时会添加参考)为我指明了正确的方向。
我的问题的解决方案如下:
var jobInJson = JsonConvert.SerializeObject(job);
var json = new StringContent(jobInJson, Encoding.UTF8);
json.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json; odata=verbose");
var flurClient = GetBaseUrlForOperations("Jobs");
return await flurClient.PostAsync(json).ReceiveJson<Job>();
编辑:找到相关的 SO 问题:
这个答案已经过时了。升级到 latest version(2.0 或更高版本),问题就消失了。
原来 real issue 与 System.Net.Http
API 验证 headers 的方式有关。它区分了 request-level headers 和 content-level headers,我一直觉得这有点奇怪,因为原始 HTTP 没有这样的区分(除了可能在多部分场景中) . Flurl 的 WithHeader
将 headers 添加到 HttpRequestMessage
object 但未通过 Content-Type
的验证,它希望将其添加到 HttpContent
object.
这些 API 确实允许您跳过验证,虽然 Flurl 不直接公开它,但您可以很容易地进入幕后,而不会破坏流畅的链条:
return await GetBaseUrlForGetOperations("Jobs")
.ConfigureHttpClient(c => c.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json;odata=verbose"))
.PostJsonAsync(new { ... })
.ReceiveJson<Job>();
这可能是做你需要做的事情的最好方法,并且仍然利用 Flurl 的优点,即不必直接处理序列化、HttpContent
objects 等
基于这个问题,我强烈考虑更改 Flurl 的 AddHeader(s)
实现以使用 TryAddWithoutValidation
。
public static class Utils
{
public static IFlurlClient GetBaseUrlForOperations(string resource)
{
var _apiUrl = "https://api.mobile.azure.com/v0.1/apps/";
var url = _apiUrl
.AppendPathSegment("Red-Space")
.AppendPathSegment("HD")
.AppendPathSegment("push")
.AppendPathSegment("notifications")
.WithHeader("Accept", "application/json")
.WithHeader("X-API-Token", "myapitocken");
return url;
}
public static async Task Invia()
{
FlurlClient _client;
PushMessage pushMessage = new PushMessage();
pushMessage.notification_content = new NotificationContent();
try
{
var flurClient = Utils.GetBaseUrlForOperations("risorsa");
// News news = (News)contentService.GetById(node.Id);
//pushMessage.notification_target.type = "";
pushMessage.notification_content.name = "A2";
// pushMessage.notification_content.title = node.GetValue("TitoloNews").ToString();
pushMessage.notification_content.title = "Titolo";
pushMessage.notification_content.body = "Contenuto";
var jobInJson = JsonConvert.SerializeObject(pushMessage);
var json = new StringContent(jobInJson, Encoding.UTF8);
json.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
dynamic data2 = await flurClient.PostAsync(json).ReceiveJson();
var expandoDic = (IDictionary<string, object>)data2;
var name = expandoDic["notification_id"];
Console.WriteLine(name);
}
catch (FlurlHttpTimeoutException ex)
{
Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType + " " + ex);
}
catch (FlurlHttpException ex)
{
Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType + " " + ex);
if (ex.Call.Response != null)
Console.WriteLine("Failed with response code " + ex.Call.Response.StatusCode);
else
Console.WriteLine("Totally failed before getting a response! " + ex.Message);
}
catch (Exception ex)
{
Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType + " " + ex);
}
}
}
public class NotificationTarget
{
public string type { get; set; }
}
public class CustomData {}
public class NotificationContent
{
public string name { get; set; }
public string title { get; set; }
public string body { get; set; }
public CustomData custom_data { get; set; }
}
public class PushMessage
{
public NotificationTarget notification_target { get; set; }
public NotificationContent notification_content { get; set; }
}
我可以 post 对同一个问题给出 3 个答案吗? :)
Upgrade. Flurl.Http 2.0 包括以下对 headers 的增强:
WithHeader(s)
现在在后台使用 TryAddWithoutValidation
。仅通过该更改,OP 的代码将按最初 posted.
工作
Headers现在设置在请求级别,解决了another known issue.
当使用 SetHeaders
和 object 符号时,header 名称中的 underscores in property names will be converted to hyphens,因为 header 中的连字符非常C# 标识符中不允许使用下划线和连字符。
这对你的情况很有用:
.WithHeaders(new {
x_ms_version = "2.11",
Accept = "application/json"
});
我正在使用 flurl 提交 HTTP 请求,这非常有用。现在我需要将某些请求的“Content-Type” header 更改为 "application/json;odata=verbose"
public async Task<Job> AddJob()
{
var flurlClient = GetBaseUrlForGetOperations("Jobs").WithHeader("Content-Type", "application/json;odata=verbose");
return await flurlClient.PostJsonAsync(new
{
//Some parameters here which are not the problem since tested with Postman
}).ReceiveJson<Job>();
}
private IFlurlClient GetBaseUrlForOperations(string resource)
{
var url = _azureApiUrl
.AppendPathSegment("api")
.AppendPathSegment(resource)
.WithOAuthBearerToken(AzureAuthentication.AccessToken)
.WithHeader("x-ms-version", "2.11")
.WithHeader("Accept", "application/json");
return url;
}
你可以看到我是如何尝试添加上面的 header (.WithHeader("Content-Type", "application/json;odata=verbose")
)
不幸的是,这给了我以下错误:
"InvalidOperationException: Misused header name. Make sure request headers are used with HttpRequestMessage, response headers with HttpResponseMessage, and content headers with HttpContent objects."
我也试过flurl的"ConfigureHttpClient"方法但是找不到how/where来设置内容类型header.
我不是 OData 专家,我不知道 API 你叫什么(SharePoint?),但根据我见过的大多数例子,你通常想要做的是要求服务器在响应中发送详细的 OData,而不是声明您在请求中发送它。换句话说,您想在 Accept header 上设置 ;odata=verbose
位,而不是 Content-Type . application/json
对 Content-Type 应该足够好了,Flurl 会自动为您设置,所以只需尝试这个更改,看看它是否有效:
.WithHeader("Accept", "application/json;odata=verbose");
评论和我发现的另一个 post(当我再次找到它时会添加参考)为我指明了正确的方向。 我的问题的解决方案如下:
var jobInJson = JsonConvert.SerializeObject(job);
var json = new StringContent(jobInJson, Encoding.UTF8);
json.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json; odata=verbose");
var flurClient = GetBaseUrlForOperations("Jobs");
return await flurClient.PostAsync(json).ReceiveJson<Job>();
编辑:找到相关的 SO 问题:
这个答案已经过时了。升级到 latest version(2.0 或更高版本),问题就消失了。
原来 real issue 与 System.Net.Http
API 验证 headers 的方式有关。它区分了 request-level headers 和 content-level headers,我一直觉得这有点奇怪,因为原始 HTTP 没有这样的区分(除了可能在多部分场景中) . Flurl 的 WithHeader
将 headers 添加到 HttpRequestMessage
object 但未通过 Content-Type
的验证,它希望将其添加到 HttpContent
object.
这些 API 确实允许您跳过验证,虽然 Flurl 不直接公开它,但您可以很容易地进入幕后,而不会破坏流畅的链条:
return await GetBaseUrlForGetOperations("Jobs")
.ConfigureHttpClient(c => c.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json;odata=verbose"))
.PostJsonAsync(new { ... })
.ReceiveJson<Job>();
这可能是做你需要做的事情的最好方法,并且仍然利用 Flurl 的优点,即不必直接处理序列化、HttpContent
objects 等
基于这个问题,我强烈考虑更改 Flurl 的 AddHeader(s)
实现以使用 TryAddWithoutValidation
。
public static class Utils
{
public static IFlurlClient GetBaseUrlForOperations(string resource)
{
var _apiUrl = "https://api.mobile.azure.com/v0.1/apps/";
var url = _apiUrl
.AppendPathSegment("Red-Space")
.AppendPathSegment("HD")
.AppendPathSegment("push")
.AppendPathSegment("notifications")
.WithHeader("Accept", "application/json")
.WithHeader("X-API-Token", "myapitocken");
return url;
}
public static async Task Invia()
{
FlurlClient _client;
PushMessage pushMessage = new PushMessage();
pushMessage.notification_content = new NotificationContent();
try
{
var flurClient = Utils.GetBaseUrlForOperations("risorsa");
// News news = (News)contentService.GetById(node.Id);
//pushMessage.notification_target.type = "";
pushMessage.notification_content.name = "A2";
// pushMessage.notification_content.title = node.GetValue("TitoloNews").ToString();
pushMessage.notification_content.title = "Titolo";
pushMessage.notification_content.body = "Contenuto";
var jobInJson = JsonConvert.SerializeObject(pushMessage);
var json = new StringContent(jobInJson, Encoding.UTF8);
json.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
dynamic data2 = await flurClient.PostAsync(json).ReceiveJson();
var expandoDic = (IDictionary<string, object>)data2;
var name = expandoDic["notification_id"];
Console.WriteLine(name);
}
catch (FlurlHttpTimeoutException ex)
{
Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType + " " + ex);
}
catch (FlurlHttpException ex)
{
Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType + " " + ex);
if (ex.Call.Response != null)
Console.WriteLine("Failed with response code " + ex.Call.Response.StatusCode);
else
Console.WriteLine("Totally failed before getting a response! " + ex.Message);
}
catch (Exception ex)
{
Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType + " " + ex);
}
}
}
public class NotificationTarget
{
public string type { get; set; }
}
public class CustomData {}
public class NotificationContent
{
public string name { get; set; }
public string title { get; set; }
public string body { get; set; }
public CustomData custom_data { get; set; }
}
public class PushMessage
{
public NotificationTarget notification_target { get; set; }
public NotificationContent notification_content { get; set; }
}
我可以 post 对同一个问题给出 3 个答案吗? :)
Upgrade. Flurl.Http 2.0 包括以下对 headers 的增强:
WithHeader(s)
现在在后台使用TryAddWithoutValidation
。仅通过该更改,OP 的代码将按最初 posted. 工作
Headers现在设置在请求级别,解决了another known issue.
当使用
SetHeaders
和 object 符号时,header 名称中的 underscores in property names will be converted to hyphens,因为 header 中的连字符非常C# 标识符中不允许使用下划线和连字符。
这对你的情况很有用:
.WithHeaders(new {
x_ms_version = "2.11",
Accept = "application/json"
});