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控制器所發布的接口信息,如下所示。

 

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM