Post

Maui最佳实践之 Http请求

功能说明

  1. 应用中应该对网络访问无感,即封装httpclient
  2. 对请求根据业务进行分类,并封装配置过程

HttpClient

httpClient 是 .net 系统库提供的网络访问类。

可以创建一个 httpClient 然后,使用 Get、Post 等方法访问 Uri

HttpClient还接受一个消息处理器 HttpMessageHandler,用来进行基本配置。

HttpClient-官方文档

HttpMessageHandler-官方文档

封装提供网络请求的服务

这里使用“单例模式”对httpclient进行封装。虽然官方提供了工厂方法HttpClientFactory,但该方案只适用于无状态短期的http请求。对于客户端应用来说,这样做可能导致频繁创建httpclient,进而导致系统套接字资源耗尽。

RequestProvider 请求构造器

以下构造器接口,支持Token验证和设备验证。以及自定义请求头等功能。

1
2
3
4
5
6
7
8
9
10
11
12
public interface IRequestProvider{
  Task<TResult> GetAsync<TResult>(string uri, string token="");
  Task<TResult> PostAsync<TResult, TResponse>(string ur, TRequest data, string token="", string header="");
  
  Task<bool> PostAsync<TRequest>(string uri, TRequest data, string token = "", string header = "");

  Task<TResult> PostAsync<TResult>(string uri, string data, string clientId, string clientSecret);

  Task<TResult> PutAsync<TResult>(string uri, TResult data, string token = "", string header = "");

  Task DeleteAsync(string uri, string token = "");
}

httpClient 使用懒加载单例模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class RequestProvider(HttpMessageHandler _messageHandler) : IRequestProvider{
  // 单例
  private readonly Lazy<HttpClient> _httpClient = 
    new(()=>{
      var httpClient = messageHander is not null ? new HttpClient(messageHanlder) : HttpClient();
      httpClient.DefaultRequestHanders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
      return httpClient;
    }, LazyThreadSafetyMode.ExecutionAndPublication);
  
  public async Task<TResult> GetAsync<TResult>(string uri, string token=""){
    var httpClient = GetOrCreateHttpClient(token);
    using var response = await httpClient.GetAsync(uri).ConfigureAwait(false);
		// 异步执行的最佳实践
    await HandleResponse(response).ConfigureAwait(false);
    var result = await response.Content.ReadFromJsonAsync<TResult>(_jsonSerializerContext).ConfigureAwait(false);

    return result;
  }
}

Lazy<T> 延迟初始化类型

延迟初始化使得在类型初始化时,该成员不会立刻被创建,而是只有在需要时创建。

创建Lazy需要提供一个委托参数,即如何实例化该成员的委托。

对业务请求进行分割

在eShop项目中,对“商品目录”“购物车”“身份验证”等业务进行了分割

这里以商品目录为例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public interface ICatalogService
{
    Task<IEnumerable<CatalogBrand>> GetCatalogBrandAsync();
    Task<IEnumerable<CatalogItem>> FilterAsync(int catalogBrandId, int catalogTypeId);
    Task<IEnumerable<CatalogType>> GetCatalogTypeAsync();
    Task<IEnumerable<CatalogItem>> GetCatalogAsync();

    Task<CatalogItem> GetCatalogItemAsync(int catalogItemId);
}

// FilterAsync 的实现
public async Task<IEnumerable<CatalogItem>> FilterAsync(int catalogBrandId, int catalogTypeId)
{
    var uri = UriHelper.CombineUri(_settingsService.GatewayCatalogEndpointBase,
        $"{ApiUrlBase}/items/type/{catalogTypeId}/brand/{catalogBrandId}?PageSize=100&PageIndex=0&{ApiVersion}");

    var catalog = await _requestProvider.GetAsync<CatalogRoot>(uri).ConfigureAwait(false);

    return catalog?.Data ?? Enumerable.Empty<CatalogItem>();
}
This post is licensed under CC BY 4.0 by the author.