说明

新版本中,官方已经提供了默认的统一接口实现(PasteApeart中的 PasteDefaultAppService),用户项目不再需要自己写DefaultAppService了!

之前那种把几千行反射代码复制到项目里的做法已经过时,通用的增删改查、模型读取、分页查询等能力全部由官方组件内置提供。项目里只需要做两件事:

  1. 在Module中注册官方默认服务
  2. 写一个很薄的DefaultApiAppService(只做路由转发,逻辑全部委托给官方)

需要个性化?比如新增前检查、修改状态后的动作、自定义实体映射名称等,继承 PasteDefaultAppService 重写对应的虚方法即可,不需要动通用逻辑!

完整案例参考:abp_example/ExampleForm.Application

一、Module中注册服务

在你的XXX.ApplicationModule(比如ExampleFormApplicationModule)的ConfigureServices中加入:

//注册默认的辅助类:传入实体程序集、DTO程序集、外表转换的扩展方法类
context.Services.AddSingleton<IPasteDefaultHelper>(sp =>
    new PasteDefaultHelper(
        sp.GetRequiredService<IMemoryCache>(),
        typeof(UserInfo).Assembly.GetTypes(),     //实体所在的程序集
        typeof(UserInfoDto).Assembly.GetTypes(),  //DTO所在的程序集
        typeof(LinqExtend)                        //外表ToShortXXX等扩展方法
    )
);

//直接使用官方默认实现,不需要任何自定义时用这个
context.Services.AddScoped<IPasteDefaultAppService, PasteDefaultAppService>();

//如果需要自定义(写入前检查、绑定后检查等),继承PasteDefaultAppService后注册自己的实现
//context.Services.AddScoped<IPasteDefaultAppService, ExampleFormDefaultService>();

二、DefaultApiAppService(路由薄壳)

在XXX.Application项目下新建DefaultApiAppService.cs,把下面的代码放进去,结合自己的项目调整命名空间和基类即可!所有方法只是转发,真正的逻辑在官方的IPasteDefaultAppService中:

using Microsoft.AspNetCore.Mvc;
using PasteApeart.Application;
using PasteApeart.Application.Contracts;
using PasteApeart.Helper;
using System.Text;

namespace ExampleForm.Application
{
    /// <summary>
    /// 默认读取 这个很关键,实现了统一的新增 更新 详细 列表规范读取 相当于兜底的!
    /// </summary>
    [TypeFilter(typeof(RoleAttribute), Arguments = new object[] { "data", "view" })]
    public class DefaultApiAppService : ExampleFormAppService
    {

        private readonly IPasteDefaultAppService _appService;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="appService"></param>
        public DefaultApiAppService(IPasteDefaultAppService appService)
        {
            _appService = appService;
        }


        /// <summary>
        /// 获取新增格式
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        [TypeFilter(typeof(RoleAttribute), Arguments = new object[] { "data", "view" })]
        [Route("/api/app/{entityName}/readAddModel", Order = 10)]
        public async Task<VoloModelInfo> ReadAddModel([FromRoute] string entityName)
        {

            return await _appService.ReadAddModel(entityName);

        }

        /// <summary>
        /// 获取更新数据
        /// </summary>
        /// <param name="entityName"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        /// <exception cref="PasteCodeException"></exception>
        [HttpGet]
        [TypeFilter(typeof(RoleAttribute), Arguments = new object[] { "data", "view" })]
        [Route("/api/app/{entityName}/{id}/readUpdateModel", Order = 10)]
        public async Task<VoloModelInfo> ReadUpdateModel([FromRoute] string entityName, [FromRoute] string id)
        {
            return await _appService.ReadUpdateModel(entityName, id);

        }

        /// <summary>
        /// 获取详细
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        [Route("/api/app/{entityName}/{id}/readDetailModel", Order = 10)]
        [TypeFilter(typeof(RoleAttribute), Arguments = new object[] { "data", "view" })]
        public async Task<VoloModelInfo> ReadDetailModel([FromRoute] string entityName, [FromRoute] string id)
        {

            return await _appService.ReadDetailModel(entityName, id);

        }

        /// <summary>
        /// 获取列表格式
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        [Route("/api/app/{entityName}/readListModel", Order = 10)]
        [TypeFilter(typeof(RoleAttribute), Arguments = new object[] { "data", "view" })]
        public async Task<VoloModelInfo> ReadListModel([FromRoute] string entityName, string searchName = "")
        {
            return await _appService.ReadListModel(entityName, searchName);
        }

        /// <summary>
        /// 获取列表数据
        /// </summary>
        /// <param name="entityName"></param>
        /// <param name="searchName"></param>
        /// <returns></returns>
        /// <exception cref="PasteCodeException"></exception>
        [HttpGet]
        [Route("/api/app/{entityName}/page", Order = 10)]
        [TypeFilter(typeof(RoleAttribute), Arguments = new object[] { "data", "view" })]
        public async Task<dynamic> Page([FromRoute] string entityName, string searchName = "")
        {
            var query_string = base._httpContext.Request.QueryString.ToString();

            return await _appService.Page(entityName, query_string, searchName);

        }

        /// <summary>
        /// 通用创建方法
        /// </summary>
        /// <param name="entityName"></param>
        /// <returns></returns>
        /// <exception cref="PasteCodeException"></exception>
        [HttpPost]
        [Route("/api/app/{entityName}/item", Order = 10)]
        [TypeFilter(typeof(RoleAttribute), Arguments = new object[] { "data", "add" })]
        public async Task<dynamic> CreateItemAsync([FromRoute] string entityName)
        {
            using var read = new StreamReader(_httpContext.Request.Body, Encoding.UTF8);
            var inputString = await read.ReadToEndAsync();
            return await _appService.CreateItemAsync(entityName, inputString);

        }

        /// <summary>
        /// 通用更新方法
        /// </summary>
        /// <param name="entityName"></param>
        /// <returns></returns>
        /// <exception cref="PasteCodeException"></exception>
        [HttpPost]
        [Route("/api/app/{entityName}/updateItem", Order = 10)]
        [TypeFilter(typeof(RoleAttribute), Arguments = new object[] { "data", "edit" })]
        public async Task<object> UpdateItemAsync([FromRoute] string entityName)
        {
            using var read = new StreamReader(_httpContext.Request.Body, Encoding.UTF8);
            var inputString = await read.ReadToEndAsync();

            return await _appService.UpdateItemAsync(entityName, inputString);

        }


        /// <summary>
        /// 通用状态更新方法
        /// </summary>
        /// <param name="entityName"></param>
        /// <param name="input"></param>
        /// <returns></returns>
        /// <exception cref="PasteCodeException"></exception>
        [HttpPost]
        [Route("/api/app/{entityName}/updateState", Order = 10)]
        [TypeFilter(typeof(RoleAttribute), Arguments = new object[] { "data", "state" })]
        public async Task<string> UpdateState([FromRoute] string entityName, [FromBody] InputQueryUpdateState input)
        {
            return await _appService.UpdateState(entityName, input);


        }


        /// <summary>
        /// 通用删除 遵照ListDto的PasteDisable特性
        /// </summary>
        /// <param name="entityName"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        /// <exception cref="PasteCodeException"></exception>
        [HttpPost]
        [Route("/api/app/{entityName}/{id}/remove", Order = 10)]
        [TypeFilter(typeof(RoleAttribute), Arguments = new object[] { "data", "del" })]
        public async Task<string> Remove([FromRoute] string entityName, [FromRoute] string id)
        {
            return await _appService.Remove(entityName, id);

        }



    }

}

三、个性化扩展(可选)

如果某些实体需要在默认动作前后加入自己的逻辑,继承 PasteDefaultAppService 重写虚方法即可,参考案例ExampleApeartDefaultService.cs:

public class ExampleFormDefaultService : PasteDefaultAppService
{
    public ExampleFormDefaultService(IPasteDefaultHelper defaultHelper, IExampleFormDbContext dbContext)
        : base(defaultHelper, dbContext as DbContext)
    {
        //用于自定义实现一些默认接口的动作,比如写入前检查,绑定后检查等!!!
    }

    //修改状态后 包含 绑定
    public override Task DefaultAfterUpdateStateAsync(string entityName, InputQueryUpdateState input)
    {
        return base.DefaultAfterUpdateStateAsync(entityName, input);
    }

    //数据赋值后,比如写入前检查、默认值填充等
    public override Task DefaultDataSetExtendAsync(Type entityType, object obj, string action)
    {
        return base.DefaultDataSetExtendAsync(entityType, obj, action);
    }

    //自定义对外的实体名称,比如UserInfo你想对外接口为member就在这里配置
    public override string DefaultMapperEntityName(string entityName)
    {
        return base.DefaultMapperEntityName(entityName);
    }

    //删除后的动作
    public override Task DefaultAfterRemoveAsync(string entityName, string id)
    {
        return base.DefaultAfterRemoveAsync(entityName, id);
    }
}

注册时把第二步中的官方实现替换成你的自定义实现:

context.Services.AddScoped<IPasteDefaultAppService, ExampleFormDefaultService>();

四、通用接口一览

注册并放置DefaultApiAppService后,每个实体自动获得以下接口(entityName为实体名称,支持小驼峰):

接口 方法 说明
/api/app/{entityName}/readAddModel GET 读取新增表单模型(AddDto)
/api/app/{entityName}/{id}/readUpdateModel GET 读取编辑表单模型(UpdateDto)
/api/app/{entityName}/{id}/readDetailModel GET 读取详细模型(Dto)
/api/app/{entityName}/readListModel GET 读取列表表格模型(ListDto)
/api/app/{entityName}/page GET 分页查询列表数据
/api/app/{entityName}/item POST 通用新增
/api/app/{entityName}/updateItem POST 通用更新
/api/app/{entityName}/updateState POST 通用状态更新(布尔字段开关)
/api/app/{entityName}/{id}/remove POST 通用删除(遵照ListDto的PasteDisable特性)

有个性化的实体,单独再写自己的AppService接口覆盖即可,和默认接口互不干扰!

发现更多文档

来自贴代码的公开项目