ABP开发框架前后端开发系列---(4)Web API调用类的封装和使用

在前面随笔介绍ABP应用框架的项目组织情况,以及项目中领域层各个类代码组织,以及简化了ABP框架的各个层的内容,使得我们项目结构更加清晰。上篇随笔已经介绍了字典模块中应用服务层接口的实现情况,并且通过运行WebAPI的宿主程序,可以在界面上进行接口测试了,本篇随笔基于前面介绍的基础上,介绍WebAPI调用类的封装和使用,使用包括控制台和Winform中对调用封装类的使用。在上篇随笔《ABP开发框架...

ABP开发框架前后端开发系列---(4)Web API调用类的封装和使用

在前面随笔介绍ABP应用框架的项目组织情况,以及项目中领域层各个类代码组织,以及简化了ABP框架的各个层的内容,使得我们项目结构更加清晰。上篇随笔已经介绍了字典模块中应用服务层接口的实现情况,并且通过运行Web API的宿主程序,可以在界面上进行接口测试了,本篇随笔基于前面介绍的基础上,介绍Web API调用类的封装和使用,使用包括控制台和Winform中对调用封装类的使用。

在上篇随笔《ABP开发框架前后端开发系列---(3)框架的分层和文件组织》中我绘制了改进后的ABP框架的架构图示,如下图所示。

这个项目分层里面的 03-Application.Common 应用服务通用层,我们主要放置在各个模块里面公用的DTO和应用服务接口类。有了这些DTO文件和接口类,我们就不用在客户端(如Winform客户、控制台、WPF/UWP等)重复编写这部分的内容,直接使用即可。

这些DTO文件和接口类文件,我们的主要用途是用来封装客户端调用Web API的调用类,使得我们在界面使用的时候,调用更加方便。

1)Web API调用类封装

为了更方便在控制台客户端、Winform客户端等场景下调用Web API的功能,我们需要对应用服务层抛出的Web API接口进行封装,然后结合DTO类实现一个标准的接口实现。

由于这些调用类可能在多个客户端中进行共享,因此根据我们在混合框架中积累的经验,我们把它们独立为一个项目进行管理,如下项目视图所示。

其中DictDataApiCaller 就是对应领域对象 <领域对象>ApiCaller的命名规则。

如对于字典模块的API封装类,它们继承一个相同的基类,然后实现特殊的自定义接口即可,这样可以减少常规的Create、Get、GetAll、Update、Delete等操作的代码,这些全部由调用基类进行处理,而只需要实现自定义的接口调用即可。如下是字典模块DictType和DictData两个业务对象的API封装关系。

如对于字典类型的API封装类定义代码如下所示。

 /// <summary> /// 字典类型对象的Web API调用处理 /// </summary> public class DictTypeApiCaller : AsyncCrudApiCaller<DictTypeDto, string, DictTypePagedDto, CreateDictTypeDto, DictTypeDto>, IDictTypeAppService {  /// <summary>  /// 提供单件对象使用  /// </summary>  public static DictTypeApiCaller Instance  {get{ return Singleton<DictTypeApiCaller>.Instance;}  }......

这里我们可以通过单件的方式来使用字典类型API的封装类实例DictTypeApiCaller.Instance

对于Web API的调用,我们知道,一般需要使用WebClient或者HttpRequest的底层类进行Url的访问处理,通过提供相应的数据,获取对应的返回结果。

而对于操作方法的类型,是使用POST、GET、INPUT、DELETE的不同,需要看具体的接口,我们可以通过Swagger UI 呈现出来的进行处理即可,如下所示的动作类型。

如果处理动作不匹配,如本来是Post的用Get方法,或者是Delete的用Post方法,都会出错。

在Abp.Web.Api项目里面有一个AbpWebApiClient的封装方法,里面实现了POST方法,可以参考来做对应的WebClient的封装调用。

我在它的基础上扩展了实现方法,包括了Get、Put、Delete方法的调用。

我们使用的时候,初始化它就可以了。

apiClient = new AbpWebApiClient();

例如,我们对于常规的用户登录处理,它的API调用封装的操作代码如下所示,这个是一个POST方法。

  /// <summary>  /// 对用户身份进行认证  /// </summary>  /// <param name="username">用户名</param>  /// <param name="password">用户密码</param>  /// <returns></returns>  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;  }

对于业务接口来说,我们都是基于约定的规则来命名接口名称和地址的,如对于GetAll这个方法来说,字典类型的地址如下所示。

/api/services/app/DictData/GetAll

另外还包括服务器的基础地址,从而构建一个完整的调用地址如下所示。

http://localhost:21021/api/services/app/DictData/GetAll

由于这些规则确定,因此我们可以通过动态构建这个API地址即可。

string url = GetActionUrl(MethodBase.GetCurrentMethod());//获取访问API的地址(未包含参数)url  = string.Format("?SkipCount={0}&MaxResultCount={1}", dto.SkipCount, dto.MaxResultCount);

而对于GetAll函数来说,这个定义如下所示。

Task<PagedResultDto<TEntityDto>> GetAll(TGetAllInput input)

它是需要根据一定的条件进行查询的,不仅仅是SkipCount 和MaxResultCount两个属性,因此我们需要动态组合它的url参数,因此建立一个辅助类来动态构建这些输入参数地址。

  /// <summary>  /// 获取所有对象列表  /// </summary>  /// <param name="input">获取所有条件</param>  /// <returns></returns>  public async virtual Task<PagedResultDto<TEntityDto>> GetAll(TGetAllInput input)  {AddRequestHeaders();//加入认证的token头信息string url = GetActionUrl(MethodBase.GetCurrentMethod());//获取访问API的地址(未包含参数)url = GetUrlParam(input, url);var result = await apiClient.GetAsync<PagedResultDto<TEntityDto>>(url);return result;  }

这样我们这个API的调用封装类的基类就实现了常规的功能了。效果如下所示。

而字典类型的API封装类,我们只需要实现特定的自定义接口即可,省却我们很多的工作量。

namespace MyProject.Caller{ /// <summary> /// 字典类型对象的Web API调用处理 /// </summary> public class DictTypeApiCaller : AsyncCrudApiCaller<DictTypeDto, string, DictTypePagedDto, CreateDictTypeDto, DictTypeDto>, IDictTypeAppService {  /// <summary>  /// 提供单件对象使用  /// </summary>  public static DictTypeApiCaller Instance  {get{ return Singleton<DictTypeApiCaller>.Instance;}  }  /// <summary>  /// 默认构造函数  /// </summary>  public DictTypeApiCaller()  {this.DomainName = "DictType";//指定域对象名称,用于组装接口地址  }  public async Task<Dictionary<string, string>> GetAllType(string dictTypeId)  {AddRequestHeaders();//加入认证的token头信息string url = GetActionUrl(MethodBase.GetCurrentMethod());//
源文地址:https://www.guoxiongfei.cn/cntech/18368.html