说明
新版本中,官方已经提供了默认的统一接口实现(PasteApeart中的 PasteDefaultAppService),用户项目不再需要自己写DefaultAppService了!
之前那种把几千行反射代码复制到项目里的做法已经过时,通用的增删改查、模型读取、分页查询等能力全部由官方组件内置提供。项目里只需要做两件事:
- 在Module中注册官方默认服务
- 写一个很薄的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接口覆盖即可,和默认接口互不干扰!