Skip to content

动态 Api

配置动态 Api

在 MyApp.Host 项目下的Program.cs文件中配置动态 Api

cs
new HostApp(new HostAppOptions
{
    ConfigureDynamicApi = options =>
    {
        // 接口以小驼峰方式命名
        options.NamingConvention = NamingConventionEnum.CamelCase;

        // 指定全局默认的 api 前缀
        options.DefaultApiPrefix = "api";

        /*
        * 清空API结尾,不删除API结尾;
        * 若不清空 CreatUserAsync 将变为 CreateUser
        */
        options.RemoveActionPostfixes.Clear();

        // 自定义 ActionName 处理函数;
        options.GetRestFulActionName = (actionName) => actionName;


        // 指定程序集 配置所有的api请求方式都为 POST
        options.AddAssemblyOptions(GetType().Assembly, httpVerb: "POST");

        /*
        * 指定程序集 配置 url 前缀为 api
        * 如: http://localhost:8000/api/User/CreateUser
        */
        options.AddAssemblyOptions(GetType().Assembly, apiPreFix: "api");

        /*
        * 指定程序集 配置 url 前缀为 api, 且所有请求方式都为POST
        * 如: http://localhost:8000/api/User/CreateUser
        */
        options.AddAssemblyOptions(GetType().Assembly, apiPreFix: "api", httpVerb: "POST");

        //生成请求方式 ["方法名开头"] = "动词"
        AppConsts.HttpVerbs = new Dictionary<string, string>()
        {
           ["add"] = "POST",
           ["create"] = "POST",
           ["insert"] = "POST",
           ["submit"] = "POST",
           ["post"] = "POST",

           ["get"] = "GET",
           ["find"] = "GET",
           ["fetch"] = "GET",
           ["query"] = "GET",

           ["update"] = "PUT",
           ["change"] = "PUT",
           ["put"] = "PUT",
           ["batch"] = "PUT",

           ["delete"] = "DELETE",
           ["soft"] = "DELETE",
           ["remove"] = "DELETE",
           ["clear"] = "DELETE",
        };
    }
}).Run(args);

根据前缀动词没有匹配到请求方式则使用HttpPost方式请求

NamingConvention 接口命名可选值:

  • CamelCase:小驼峰命名 camelCase
  • PascalCase:大驼峰命名 PascalCase
  • SnakeCase:蛇形命名 snake_case
  • KebabCase:烤肉串命名 kebab-case
  • ExtensionCase:拓展名命名 extension.case
  • Custom:自定义接口名称,通过 options.GetRestFulActionName = (actionName) => actionName;设置

使用动态 Api

在服务上增加 [DynamicApi(Area = ApiConsts.AreaName)] 特性,且继承 IDynamicApi 接口自动生成动态 Api

cs
/// <summary>
/// 模块服务
/// </summary>
[DynamicApi(Area = ApiConsts.AreaName)]
public class ModuleService : BaseService, IModuleService, IDynamicApi
{
    /// <summary>
    /// 查询分页
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [HttpPost]
    public async Task<PageOutput<ModuleListOutput>> GetPageAsync(PageInput<ModuleGetPageDto> input)
    {
        var key = input.Filter?.Name;

        var list = await _moduleRepository.Select
        .WhereIf(key.NotNull(), a => a.Name.Contains(key))
        .Count(out var total)
        .OrderByDescending(true, c => c.Id)
        .Page(input.CurrentPage, input.PageSize)
        .ToListAsync<ModuleListOutput>();

        var data = new PageOutput<ModuleListOutput>()
        {
            List = list,
            Total = total
        };

        return data;
    }
}

提示

ApiConsts.AreaName 定义/api/app/module/get 路径中的app

动态 Api 会根据前缀动词生成请求方式,使用[HttpPost]特性可更改请求方式。

新接口文档地址:http://localhost:8000/admin/index.html

swagger 接口文档地址:http://localhost:8000/admin/swagger/index.html

如果服务是动态 Api,开启事物的方法务必定义为virtual虚方法,才能正常使用事物拦截。

禁用动态 Api

在服务方法上添加[NonAction]特性禁用生成动态 Api,变成服务公共方法使用

cs
[NonAction]
public async Task BatchSoftDeleteAsync(long[] ids)
{
    await _moduleRepository.SoftDeleteAsync(ids);
}

动态 Api 重命名

在服务方法上添加以下特性重命名

api 路径需使用绝对路径,才能正常重命名

cs
[HttpPost(template: "/api/[area]/[controller]/[action]")]
public async Task BatchSoftDeleteAsync(long[] ids)
{
    await _moduleRepository.SoftDeleteAsync(ids);
}

动态 Api 排序

在服务上添加[Order]特性排序

值越小该服务越靠前

cs
/// <summary>
/// 模块服务
/// </summary>
[Order(1010)]
[DynamicApi(Area = ApiConsts.AreaName)]
public class ModuleService : BaseService, IModuleService, IDynamicApi
{

}

在服务方法上添加[Order]特性排序

值越大该服务方法越靠前

cs
[Order(1010)]
public async Task BatchSoftDeleteAsync(long[] ids)
{
    await _moduleRepository.SoftDeleteAsync(ids);
}

动态 Api 多分组

在服务上添加[DynamicApi]特性指定不同分组

cs
/// <summary>
/// 模块服务
/// </summary>
[DynamicApi(Area = ApiConsts.AreaName, GroupNames = new string[] { AdminConsts.AreaName })]
public class ModuleService : BaseService, IModuleService, IDynamicApi
{

}

在服务方法上添加[ApiGroup]特性指定不同分组

接口不分组:[ApiGroup(NonGroup = true)]

cs
[ApiGroup(ApiConsts.AreaName, AdminConsts.AreaName)]
//[ApiGroup(GroupNames = new string[] { ApiConsts.AreaName, AdminConsts.AreaName })]
public async Task BatchSoftDeleteAsync(long[] ids)
{
    await _moduleRepository.SoftDeleteAsync(ids);
}

动态 Api 返回当前数据

不想返回统一包装的数据格式

json
{
    "success": true,
    "code": null,
    "msg": null,
    "data": 1
}

在服务方法上添加[NonFormatResult]特性禁用统一包装的数据,只返回当前数据

cs
[NonFormatResult]
public async Task<long> AddAsync(ModuleAddInput input)
{
    var entity = input.Adapt<ModuleEntity>();
    await _moduleRepository.InsertAsync(entity);
    return entity.Id;
}

动态 Api 授权 8.3.0

在服务方法上添加 [ApiAccess("权限点编码")] 特性

提示

[ApiAccess("权限点编码", All = true)]

All 默认 false 满足任意一个权限点编码可访问,为 true 则需要全部满足才可访问

cs
[ApiAccess("admin:dict:export")]
public async Task<ActionResult> ExportListAsync()
{
    ...
}

不生成 Swagger 接口文档

当你有一些 Api 接口并不希望对外公开或不需要在接口文档中显示时,可以通过在接口方法上添加 [ApiExplorerSettings(IgnoreApi = true)] 来实现。

cs
[ApiExplorerSettings(IgnoreApi = true)]
public async Task GetInternalDataForPublicAsync()
{
    ...
}