在较早期的随笔《ABP 开发框架前后端开发系列 ---(5)web API 调用类在 Winform 项目中的使用》已经介绍了 Web API 调用类的封装处理, 虽然这些调用类我们可以使用代码生成工具快速生成, 不过自定义接口, 还是需要我们对这些接口进行实现, 以便发起对 Web API 的调用, 并获得相应的数据返回. 本篇随笔介绍使用 API 调用类的封装类, 进行函数的抽象, 根据方法名称的推断, 构建 URL 或者 WebClient 的请求类型, 从而实现所有 API 调用函数的简化处理.
1,ABP 框架服务端和客户端的处理
ABP 框架的架构图示, 如下图所示 (以字典模块为例说明)
针对 Web API 接口调用的封装, 为了适应客户端快速调用的目的, 这个封装作为一个独立的封装层, 以方便各个模块之间进行共同调用.
而 ABP 的 Web API 调用类则需要对 Web API 接口调用进行封装, 如下所示.
如对于字典模块的 API 封装类, 它们继承一个相同的基类, 然后实现特殊的自定义接口即可, 这样可以减少常规的 Create,Get,GetAll,Update,Delete 等操作的代码, 这些全部由调用基类进行处理, 而只需要实现自定义的接口调用即可.
2,Web API 调用类的简化处理
我们对于常规的 Web API 调用接口处理, 如下代码所示.
- public async virtual Task<AuthenticateResult> Authenticate(string username, string password)
- {
- var url = string.Format("{0}/api/TokenAuth/Authenticate", ServerRootAddress);
- var input = new
- {
- UsernameOrEmailAddress = username,
- Password = password
- };
- var result = await apiClient.PostAsync<AuthenticateResult>(url, input);
- return result;
- }
这种方法的处理, 就需要自己拼接 URL 地址, 以及传递相关的参数, 一般情况下, 我们的 Web API Caller 层类的函数和 Web API 控制器的方法是一一对应的, 因此方法名称可以通过对当前接口名称的推断进行获得, 如下所示.
- public async Task<bool> ChangePassword(ChangePasswordDto input)
- {
- AddRequestHeaders();// 加入认证的 token 头信息
- string url = GetActionUrl(MethodBase.GetCurrentMethod());// 获取访问 API 的地址 (未包含参数)
- return await apiClient.PostAsync<bool>(url, input);
- }
函数 AddRequestHeaders 通过在调用前增加对应的 AccessToken 信息, 然后 URL 通过当前方法的推断即可构建一个完整的 URL, 但是这个也仅仅是针对 POST 的方法, 因为 ABP 框架根据方法的名称前缀的不同, 而采用 POST,GET,Delete,PUT 等不同的 HTTP 处理操作.
如 GET 方法, 则是需要使用 GET 请求
- public async Task<List<RoleDto>> GetRolesByUser(EntityDto<long> input)
- {
- AddRequestHeaders();// 加入认证的 token 头信息
- string url = GetActionUrl(MethodBase.GetCurrentMethod());// 获取访问 API 的地址 (未包含参数)
- url = GetUrlParam(input, url);
- var result = await apiClient.GetAsync<List<RoleDto>>(url);
- return result;
- }
而对于删除方法, 则使用下面的 DELETE 请求, DELETE 和 PUT 操作, 需要把参数串联成 GET 的 URL 形式, 类似 url += string.Format("?Id={0}", id); 这样方式
- public virtual async Task Delete(TDeleteInput input)
- {
- AddRequestHeaders();// 加入认证的 token 头信息
- string url = GetActionUrl(MethodBase.GetCurrentMethod());// 获取访问 API 的地址 (未包含参数)
- url += GetUrlParam(input, url);
- var result = await apiClient.DeleteAsync(url);
- return result;
- }
对于更新的操作, 使用了 PUT 方法
- public async virtual Task<TEntityDto> Update(TUpdateInput input)
- {
- AddRequestHeaders();// 加入认证的 token 头信息
- string url = GetActionUrl(MethodBase.GetCurrentMethod());// 获取访问 API 的地址 (未包含参数)
- var result = await apiClient.PutAsync<TEntityDto>(url, input, null);
- return result;
- }
上面这些方法, 我们根据规律, 其实可以进一步进行简化, 因为这些操作大多数类似的.
首先我们看到变化的地方, 就是根据方法的前缀采用 GET,POST,DELETE,PUT 方法, 还有就是 URL 串联字符串的不同, 对于 GET,Delete 方法, 参数使用的是组成 URL 方式, 参数使用的是 JSON 提交内容方式.
根据这些变化, 我们在基类提炼一个统一的处理方法 DoActionAsync 来处理这些不同的操作.
- /// <summary>
- /// 根据方法名称自动执行 GET/POST/PUT/DELETE 请求方法
- /// </summary>
- /// <param name="method"></param>
- /// <param name="input"></param>
- protected virtual async Task DoActionAsync(MethodBase method, object input = null)
- {
- await DoActionAsync<object>(method, input);
- }
- /// <summary>
- /// 根据方法名称自动执行 GET/POST/PUT/DELETE 请求方法
- /// </summary>
- /// <param name="method"></param>
- /// <param name="input"></param>
- protected virtual async Task<TResult> DoActionAsync<TResult>(MethodBase method, object input = null)
- {
- AddRequestHeaders();// 加入认证的 token 头信息
- string action = GetMethodName(method);
- var url = string.Format("{0}/api/services/app/{1}/{2}", ServerRootAddress, DomainName, action);// 获取访问 API 的地址 (未包含参数)
- var httpVerb = DynamicApiVerbHelper.GetConventionalVerbForMethodName(action);
- if(httpVerb == HttpVerb.Get || httpVerb == HttpVerb.Delete)
- {
- if (input != null)
- {
- //Get 和 Delete 的操作, 需要组装 URL 参数
- url = GetUrlParam(input, url);
- }
- }
- int? timeout = null;
- return await apiClient.DoActionAsync<TResult>(url, timeout, httpVerb.ToString().ToLower(), input);
- }
这样, 有了这两个函数的支持, 我们可以简化很多操作代码了.
例如对于 Update 方法, 简化的代码如下所示.
- public async virtual Task<TEntityDto> Update(TUpdateInput input)
- {
- return await DoActionAsync<TEntityDto>(MethodBase.GetCurrentMethod(), input);
- }
对于删除操作, 简化的代码依旧也是一行代码
- public virtual async Task Delete(TDeleteInput input)
- {
- await DoActionAsync(MethodBase.GetCurrentMethod(), input);
- }
GET 操作, 也是一行代码
- public async virtual Task<TEntityDto> Get(TGetInput input)
- {
- return await DoActionAsync<TEntityDto>(MethodBase.GetCurrentMethod(), input);
- }
现在你看到, 所有的客户端 API 封装类调用, 都已经非常简化, 大同小异了, 主要就是交给基类函数进行推断调用处理即可.
如用户操作的 APICaller 类的代码如下所示.
这样我们再多的接口, 都一行代码调用解决问题, 非常简单, 从此客户端封装类的实现就非常简单了, 只需要注意有没有返回值即可, 其他的都没有什么不同.
只需要注意的是, 我们定义接口的时候, 尽可能使用复杂类型对象, 这样就可以根据对象属性名称和值进行构建 URL 或者 JSON 的了.
来源: https://www.cnblogs.com/wuhuacong/p/11103110.html