How to preserve headers in the Flurl HttpClient

2019-09-11 08:13发布

问题:

I'm using the Furl.Http wrapper over the .NET Http Client. With each request my API requires a User-Agent and an Authorization header to be sent.

I would like to set that up once, rather than having to state that every time.

What I thought I would be able to do is create an instance of the FlurlClient and set the headers on it, then ResetToRoot before each request, as illustrated in this sample piece of code:

    var fc = new FlurlClient();
    fc.WithHeader("User-Agent", "Keep/1.0");
    var tokenModel = await
        "https://app.tempuri.com".AppendPathSegment("auth")
        .WithClient(fc)
        .PostUrlEncodedAsync(new { username = "you", password = "secret"})
        .ReceiveJson<TokenModel>();

    fc.WithHeader("Authorization",
         string.Format("Token {0}",tokenModel.Token));                  

fc.Url.ResetToRoot();
    var userModel = await fc.Url
                          .AppendPathSegment("auth").GetJsonAsync<UserModel>();
    Console.WriteLine(userModel.Username);

However it would appear that after the RestToRoot() the headers are no longer sent.

Is this by design? Is there a better approach to insuring these headers are sent on each request?

回答1:

The problem is the second-to-last line.

fc.Url...GetJsonAsync

FlurlClient has a reference to the Url object, but not vice-versa, so by the time you call fc.Url, you've effectively lost that reference and a new FlurlClient is created behind the scenes when you call GetJsonAsync. This is by design in that Flurl.Url is a simple builder class in the core Flurl library that can be used independently of Flurl.Http.

Here's how I'd do it:

var url = "https://app.tempuri.com";

using (var fc = new FlurlClient().WithHeader("User-Agent", "Keep/1.0")) {
    var tokenModel = await url
        .AppendPathSegment("...")
        .WithClient(fc)
        .PostUrlEncodedAsync(new { username = "you", password = "secret"})
        .ReceiveJson<TokenModel>();

    fc.WithHeader("Authorization",
        string.Format("Token {0}",tokenModel.Token));                  

    var userModel = await url
        .AppendPathSegment("...")
        .WithClient(fc)
        .GetJsonAsync<UserModel>();

    Console.WriteLine(userModel.Username);
}

A couple notes:

  1. Notice you're attaching the FlurlClient in each HTTP call.
  2. Here the url variable is just a string, so there's no need to call ResetToRoot.
  3. The using statement is not absolutely required here but it is good practice.


回答2:

There is an alternative approach that would work too. For each successive HTTP call, modify fc.Url first, then use fc to make the call:

fc.Url.ResetToRoot().AppendPathSegment(...);
var x = await fc.GetJsonAsync<T>();

In this case you would need to call ResetToRoot but not WithClient. The fluent chain is broken here but you might find it more readable. Either way works, just a matter of personal preference.