Flurl的相关配置

2386 更新于: 2021-03-15 读完约需 7 分钟

配置简介

Flurl.Http行为可通过一个分层设置系统进行配置,每一层按如下配置顺序继承/覆盖前一层:

  • FlurlHttp.GlobalSettings (静态的)
  • IFlurlClient.Settings
  • IFlurlRequest.Settings
  • HttpTest.Settings (配置的测试参数总是有效性最高)

所有4个级别的可用属性大多相同,只有少数例外。下面是一个完整的列表,包括它们的位置和不支持的位置:

Property FlurlHttp (global) HttpTest IFlurlClient IFlurlRequest
Timeout x x x x
AllowedHttpStatusRange x x x x
JsonSerializer x x x x
UrlEncodedSerializer x x x x
Redirects x x x x
BeforeCall x x x x
BeforeCallAsync x x x x
AfterCall x x x x
AfterCallAsync x x x x
OnError x x x x
OnErrorAsync x x x x
OnRedirect x x x x
OnRedirectAsync x x x x
ConnectionLeaseTimeout x x x
HttpClientFactory x x x
FlurlClientFactory x

请注意,只有没有显式地设置值时,才表示要从层次结构的上层继承。null的意思是null,而不是继承。

配置设置

设置属性都是可读/写的,但如果你想一次(原子地)改变多个配置参数,你通常应该用一个Configure*方法来做,它使用Action<Settings>委托。

配置全局默认值:

// 在用户程序启动时调用一次即可
FlurlHttp.Configure(settings => ...);

您还可以配置用于调用给定URL的FlurlClient。需要注意的是,在默认情况下同一个FlurlClient用于对同一主机的所有调用,所以这可能会影响到的不仅仅是对提供的特定URL的调用:

// 在用户程序启动时调用一次即可
FlurlHttp.ConfigureClient(url, cli => ...);

或者,你也可以显式地配置FlurlClient

flurlClient.Configure(settings => ...);

或者,链式地配置单个请求(通过字符串、Url或IFlurlRequest的扩展方法):

await url.ConfigureRequest(settings => ...).GetAsync();

还可以在测试中覆盖任何设置,无论这些设置在测试对象的哪个级别:

httpTest.Configure(settings => ...);

如果需要,你可以将任何级别的设置恢复为默认值或继承的值:

flurlClient.Settings.ResetDefaults();
FlurlHttp.GlobalSettings.ResetDefaults();

下面让我们来看看一些特定的设置。

HttpClientFactory

不要与.NET Core的IHttpClientFactory混淆;它们是非常不同的东西(Flurl的IHttpClientFactory比.NET Core的出现得早)。

对于高级场景,您可以自定义Flurl.Http构造HttpClientHttpMessageHandler实例。尽管只需要实现Flurl.Http.Configuration.IHttpClientFactory,但建议从DefaultHttpClientFactory继承,并只在需要时扩展,如下:

public class MyCustomHttpClientFactory : DefaultHttpClientFactory
{
    // 重写CreateHttpClient方法
    public override HttpClient CreateHttpClient(HttpMessageHandler handler);

    // 重写CreateMessageHandler方法
    public override HttpMessageHandler CreateMessageHandler();
}

注册全局工厂:

FlurlHttp.Configure(settings => {
    settings.HttpClientFactory = new MyCustomHttpClientFactory();
});

或者在单个FlurlClient上配置:

var cli = new FlurlClient(BASE_URL).Configure(settings => {
    settings.HttpClientFactory = new MyCustomHttpClientFactory();
});

FlurlClientFactory

IFlurlClientFactory接口定义了一个方法Get(Url),该方法负责提供应该用于调用该Url的IFlurlClient实例。在应用程序的生命周期内,默认实现了根据URL的主机/方案/端口的组合使用一个缓存的FlurlClient实例。

要改变这种行为,你可以通过直接实现IFlurlClientFactory来定义你自己的工厂,但是从FlurlClientFactoryBase继承要容易得多。它允许您通过返回基于Url的缓存键来定义缓存策略,而不必实现缓存本身,如下:

public abstract class FlurlClientFactoryBase : IFlurlClientFactory
{
    // 重写GetCacheKey方法
    protected abstract string GetCacheKey(Url url);

    // 重写Create方法(仅在需要时才调用此方法)
    protected virtual IFlurlClient Create(Url url);
}

虽然FlurlClientFactory配置设置仅在全局级别可用,但IFlurlClientFactory在依赖注入模式时也很有用。

序列化

JsonSerializer和UrlEncodedSerializer都实现了ISerializer,这是一个简单的接口,用于将对象与字符串进行序列化。

public interface ISerializer
{
    string Serialize(object obj);
    T Deserialize<T>(string s);
    T Deserialize<T>(Stream stream);
}

两者都有一个全局注册的默认实现,你可以替换它们,但不是必要的。默认的JsonSerializer实现是NewtonsoftJsonSerializer,正如您可能猜到的那样,它使用了一直流行的Json.NET序列化类库。你可以使用NewtonsoftJsonSerializer来自定义序列化配置,如下:

FlurlHttp.Configure(settings => {
    var jsonSettings = new JsonSerializerSettings
    {
        NullValueHandling = NullValueHandling.Ignore,
        ObjectCreationHandling = ObjectCreationHandling.Replace
    };
    settings.JsonSerializer = new NewtonsoftJsonSerializer(jsonSettings);
});

事件处理

将关注点(如日志记录和错误处理)与正常的逻辑流分离开,通常会使代码更加清晰。Flurl.Http为这些场景提供了一个事件模型。BeforeCall,AfterCall, OnError, OnRedirect 以及它们的*Async同名异步方法都在全局或客户端级别被定义,但如果需要的话,也可以在每个请求中定义。这些方法都采用了Action<HttpCall>委托,FlurlCall提供了你可以利用的关于调用的丰富细节:

public class FlurlCall
{
    public IFlurlRequest Request { get; set; }
    public HttpRequestMessage HttpRequestMessage { get; set; }
    public IFlurlResponse Response { get; set; }
    public HttpResponseMessage HttpResponseMessage { get; set; }
    public string RequestBody { get; }
    public FlurlRedirect Redirect { get; set; }
    public FlurlCall RedirectedFrom { get; set; }
    public Exception Exception { get; set; }
    public bool ExceptionHandled { get; set; }
    public DateTime StartedUtc { get; set; }
    public DateTime? EndedUtc { get; set; }
    public TimeSpan? Duration { get; }
    public bool Completed { get; }
    public bool Succeeded { get; }
}

与响应有关的属性值在BeforeCall中都是nullAfterCall在请求成功或者失败时都会被触发,在OnError中将ExceptionHandled设置为true可以防止出现异常。下面是一个注册全局异步错误处理程序的示例:

private async Task HandleFlurlErrorAsync(HttpCall call) {
    await LogErrorAsync(call.Exception.Message);
    call.ExceptionHandled = true;
}

FlurlHttp.Configure(settings => settings.OnErrorAsync = HandleFlurlErrorAsync);

重定向(Redirects)

像HttpClient一样,Flurl默认自动跟随3xx重定向,但是Flurl公开的设置和钩子提供了更高级别的可配置性,如下:

FlurlHttp.Configure(settings => {
    settings.Redirects.Enabled = true; // 默认值:true
    settings.Redirects.AllowSecureToInsecure = true; // 默认值:false
    settings.Redirects.ForwardAuthorizationHeader = true; // 默认值:false
    settings.Redirects.MaxAutoRedirects = 5; // 默认值:10 (连续的)
});

你还可以使用事件处理程序在每次调用的基础上配置重定向行为:

flurlClient.OnRedirect(call => {
    if (call.Redirect.Count > 5) {
        call.Redirect.Follow = false;
    }
    else {
        log.WriteInfo($"redirecting from {call.Request.Url} to {call.Redirect.Url}");
        call.Redirect.ChangeVerbToGet = (call.Response.Status == 301);
        call.Redirect.Follow = true;
    }
});

如果你只需要启用/禁用单个调用的自动重定向,你可以内联执行:

await url.WithAutoRedirect(false).GetAsync();

版权声明:本作品系原创,版权归码友网所有,如未经许可,禁止任何形式转载,违者必究。

发表评论

登录用户才能发表评论, 请 登 录 或者 注册