繁体   English   中英

如何更改FLURL客户端的HTTP请求内容类型?

[英]How to change the HTTP Request Content Type for FLURL Client?

我正在使用flurl提交HTTP请求,这非常有用。 现在我需要将一些请求的“ Content-Type”标题更改为“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;
    }

您可以看到我如何尝试添加上面的标题( .WithHeader("Content-Type", "application/json;odata=verbose")

不幸的是,这给了我以下错误:

“InvalidOperationException:Misused header name。确保请求头与HttpRequestMessage一起使用,响应头与HttpResponseMessage一起使用,内容头与HttpContent对象一起使用。”

我也尝试了flurl的“ConfigureHttpClient”方法,但无法找到设置内容类型标头的方式/位置。

这个答案已经过时了。 升级到最新版本 (2.0或更高版本 ),问题就消失了。

事实证明, 真正的问题System.Net.Http API如何验证标头有关。 它区分了请求级标题和内容级标题,我总是觉得有点奇怪,因为原始HTTP没有这样的区别(除了可能在多部分方案中)。 Flurl的WithHeader将标头添加到HttpRequestMessage对象,但是对Content-Type验证失败,它希望将其添加到HttpContent对象中。

那些API确实允许你跳过验证,虽然Flurl没有直接公开它,你可以很容易地进入引擎盖,而不会破坏流畅的链:

return await GetBaseUrlForGetOperations("Jobs")
    .ConfigureHttpClient(c => c.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json;odata=verbose"))
    .PostJsonAsync(new { ... })
    .ReceiveJson<Job>();

这可能是做你需要的最好的方法,并且仍然利用Flurl的优点,即不必直接处理序列化, HttpContent对象等。

我强烈考虑改变Flurl的AddHeader(s)实现, TryAddWithoutValidation根据此问题使用TryAddWithoutValidation

我发现的评论和另一篇文章(当我再次找到它时会添加参考文献)指出了正确的方向。 我的问题的解决方案如下:

        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问题: Azure编码作业通过REST失败

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; }
}

我不是OData专家,我不知道你在调用什么API(SharePoint?),但基于我见过的大多数示例,你通常想要做的是要求服务器发送详细的OData响应,而不是声明您在请求中发送它。 换句话说,您希望在Accept标头上设置;odata=verbose位,而不是Content-Type application/json应该足够好用于Content-Type,而Flurl会自动为你设置,所以只需尝试这个更改,看看它是否有效:

.WithHeader("Accept", "application/json;odata=verbose");

我可以发布同一个问题的3个答案吗? :)

升级。 Flurl.Http 2.0包含以下标题增强功能:

  1. WithHeader(s)现在在引擎盖下使用TryAddWithoutValidation 仅凭这一变化,OP的代码将按原始发布的方式运行。

  2. 标题现在设置在请求级别,这解决了另一个已知问题

  3. 使用带有对象表示法的SetHeaders,属性名称中的下划线将在标题名称中转换为连字符 ,因为标题中的连字符非常常见,下划线不是,并且C#标识符中不允许使用连字符。

这对您的情况很有用:

.WithHeaders(new {
    x_ms_version = "2.11",
    Accept = "application/json"
});

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM