在ABP VNext框架中對HttpApi模塊的控制器進行基類封裝
在ABP VNext框架中,HttpApi項目是我們作為Restful格式的控制器對象的封裝項目,但往往很多案例都是簡單的繼承基類控制器AbpControllerBase,而需要在每個控制器里面重寫很多類似的Create/Update/Delete/Get/GetList等常規Restful接口的調用,千篇一律的重復,本篇隨筆介紹如何對這些內容通過基類的方式實現,子類無需重復代碼,並且強類型所有的接口實現。
1、Restful接口的CRUD實現
在我們使用HttpApi項目進一步封裝ABP VNext框架的Application項目中的應用服務,作為Restful格式的控制器對象,往往都需要實現基本的Create/Update/Delete/Get/GetList等常規Restful接口的實現調用,官方很多案例也都是把這部分代碼進行重復在重復,如下所示。
例如對於客戶對象Customer的HttpApi項目控制器的代碼如下:
/// <summary>
/// 客戶信息控制器
/// </summary>
//[Area("crm")]
[RemoteService]
[ControllerName("Customer")]
[Route("api/customer")]
public class CustomerController : AbpControllerBase, ICustomerAppService
{
private readonly ICustomerAppService _customerAppService;
public CustomerController(ICustomerAppService customerAppService)
{
_customerAppService = customerAppService;
}
/// <summary>
/// 創建對象
/// </summary>
[HttpPost]
public Task<CustomerDto> CreateAsync(CreateCustomerDto input)
{
return _customerAppService.CreateAsync(input);
}
/// <summary>
/// 刪除對象
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpDelete]
[Route("{id}")]
public Task DeleteAsync(string id)
{
return _customerAppService.DeleteAsync(id);
}
/// <summary>
/// 根據ID獲取指定對象
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpGet]
[Route("{id}")]
public Task<CustomerDto> GetAsync(string id)
{
return _customerAppService.GetAsync(id);
}
/// <summary>
/// 分頁獲取列表記錄
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet]
public Task<PagedResultDto<CustomerDto>> GetListAsync(CustomerPagedDto input)
{
return _customerAppService.GetListAsync(input);
}
/// <summary>
/// 更新對象
/// </summary>
[HttpPut]
[Route("{id}")]
public Task<CustomerDto> UpdateAsync(string id, CustomerDto input)
{
return _customerAppService.UpdateAsync(id, input);
}
/// <summary>
/// 獲取字段列別名
/// </summary>
/// <returns></returns>
[HttpGet]
[Route("columnalias")]
public Task<Dictionary<string, string>> GetColumnNameAlias()
{
return _customerAppService.GetColumnNameAlias();
}
}
對於其他業務對象,這部分基本上千篇一律的重復一次,就是為了簡單的封裝一下CRUD的常規接口。
那么我們是否可以考慮通過基類的方式來抽取這部分代碼,放到基類里面去實現,以后只需要繼承該基類就完事了呢?
考慮到這些Restful的API接口實現,很多都是特定的業務對象,如上面的CustomerDto、CustomerPagedDto 等,那這些就需要通過泛型的方式指定類型給基類了。
而業務接口 ICustomerAppService 也是一個特定的業務接口,也需要傳遞給基類處理,這樣才能進行調用的。
2、HttpApi 基類控制器的實現
我們注意到上面項目的ICustomerService的接口定義如下:
/// <summary>
/// 客戶信息,應用層服務接口定義
/// </summary>
public interface ICustomerAppService :
ICrudAppService<CustomerDto, string, CustomerPagedDto, CreateCustomerDto, CustomerDto>
{
///// <summary>
///// 獲取指定條件的數量
///// </summary>
///// <param name="input">查找條件</param>
///// <returns></returns>
//Task<int> CountAsync(CustomerPagedDto input);
/// <summary>
/// 獲取字段中文別名(用於界面顯示)的字典集合
/// </summary>
/// <returns></returns>
Task<Dictionary<string, string>> GetColumnNameAlias();
}
它是繼承自ICrudAppService接口(Abp的基類接口)並傳遞幾個相關的實體類參數作為基類的接口強類型構建的。
那么我們的HttpApi 基類控制器也可以采用這種方式來傳遞對應的類型,作為基類接口的處理需要。
我們定義一個控制器基類MyAbpControllerBase,讓它繼承自常規的AbpControllerBase接口,並實現ICrudAppService接口,如下所示。
/// <summary>
/// 自定義ABP控制器基類,用於實現通用的CRUD等方法
/// </summary>
public abstract class MyAbpControllerBase<TEntityDto, TKey, TGetListInput, TCreateInput, TUpdateInput> : AbpControllerBase,
IMyCrudAppService<TEntityDto, TKey, TGetListInput, TCreateInput, TUpdateInput>
where TEntityDto : IEntityDto<TKey>
where TGetListInput : IPagedAndSortedResultRequest
where TCreateInput : IEntityDto<TKey>
where TUpdateInput : IEntityDto<TKey>
{
protected IMyCrudAppService<TEntityDto, TKey, TGetListInput, TCreateInput, TUpdateInput> _service;
public MyAbpControllerBase(IMyCrudAppService<TEntityDto, TKey, TGetListInput, TCreateInput, TUpdateInput> service)
{
_service = service;
}
這樣我們就定義好這個基類,並且通過讓它傳遞相關的業務對象和對象外鍵類型,強類型相關的接口處理,並讓它實現了相關的構造函數。
那么對應的接口實現,我們只需要調用 _service 的處理即可。
/// <summary>
/// 創建對象
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost]
public Task<TEntityDto> CreateAsync(TCreateInput input)
{
return _service.CreateAsync(input);
}
/// <summary>
/// 刪除對象
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpDelete]
[Route("{id}")]
public Task DeleteAsync(TKey id)
{
return _service.DeleteAsync(id);
}
/// <summary>
/// 獲取指定id的記錄
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpGet]
[Route("{id}")]
public Task<TEntityDto> GetAsync(TKey id)
{
return _service.GetAsync(id);
}
/// <summary>
/// 獲取條件的列表
/// </summary>
[HttpGet]
public Task<PagedResultDto<TEntityDto>> GetListAsync(TGetListInput input)
{
return _service.GetListAsync(input);
}
/// <summary>
/// 更新對象
/// </summary>
[HttpPut]
[Route("{id}")]
public Task<TEntityDto> UpdateAsync(TKey id, TUpdateInput input)
{
return _service.UpdateAsync(id, input);
}
我們還可以自己增加一些特殊的接口和基類的實現,這樣我們對於常規的接口就不需要添加重復的實現代碼了,只需要繼承基類就可以了。
子類繼承基類的代碼如下所示。
/// <summary>
/// 客戶信息控制器
/// </summary>
[RemoteService]
[ControllerName("Customer")]
[Route("api/customer")]
public class CustomerController :
MyAbpControllerBase<CustomerDto, string, CustomerPagedDto,CreateCustomerDto, CustomerDto>,
ICustomerAppService
{
private readonly ICustomerAppService _customerAppService;
public CustomerController(ICustomerAppService customerAppService) : base(customerAppService)
{
_customerAppService = customerAppService;
}
}
這樣這個CustomerController默認就具有所有相關的常規接口了,不需要千篇一律的重寫那些繁雜的代碼,清爽了很多。
而如果我們需要額外增加一些接口的處理,那么在其接口定義增加,並實現即可,如下代碼所示。
/// <summary>
/// 客戶信息,應用層服務接口定義
/// </summary>
public interface ICustomerAppService :
IMyCrudAppService<CustomerDto, string, CustomerPagedDto, CreateCustomerDto, CustomerDto>
{
/// <summary>
/// 增加的額外測試接口
/// </summary>
/// <returns></returns>
Task<bool> TestExtra();
}
HttpApi項目的實現代碼如下所示。
/// <summary>
/// 客戶信息控制器
/// </summary>
[RemoteService]
[ControllerName("Customer")]
[Route("api/customer")]
public class CustomerController :
MyAbpControllerBase<CustomerDto, string, CustomerPagedDto,CreateCustomerDto, CustomerDto>,
ICustomerAppService
{
private readonly ICustomerAppService _customerAppService;
public CustomerController(ICustomerAppService customerAppService) : base(customerAppService)
{
_customerAppService = customerAppService;
}
/// <summary>
/// 測試額外的接口調用
/// </summary>
/// <returns></returns>
[HttpGet]
[Route("test-extra")]
public async Task<bool> TestExtra()
{
return await _customerAppService.TestExtra();
}
}
啟動Swagger的查看接口界面,我們可以看到,Customer控制器所發布的接口信息,如下所示。

一切都是那么的美好,以后再也不用重復書寫或看到那些重復的,沒有技術含量的代碼了。

