ABP VNext框架中HttpApi模組 在ABP VNext框架中對HttpApi模組的控制器進行基類封裝
在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控制器所釋出的介面資訊,如下所示。
一切都是那麼的美好,以後再也不用重複書寫或看到那些重複的,沒有技術含量的程式碼了。
在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控制器所釋出的介面資訊,如下所示。
一切都是那麼的美好,以後再也不用重複書寫或看到那些重複的,沒有技術含量的程式碼了。