配置简介
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构造HttpClient和HttpMessageHandler实例。尽管只需要实现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中都是null,AfterCall在请求成功或者失败时都会被触发,在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();
                                
                    
发表评论
登录用户才能发表评论, 请 登 录 或者 注册