Skip to content
Promote Your Product

Dynamic Api

Configure Dynamic Api

Configure dynamic Api in the Program.cs file under the MyApp.Host project

cs
new HostApp(new HostAppOptions
{
    ConfigureDynamicApi = options =>
    {
        // API naming in camelCase
        options.NamingConvention = NamingConventionEnum.CamelCase;

        // Set the global default API prefix
        options.DefaultApiPrefix = "api";

        /*
        * Clear API suffix, without removing the API suffix;
        * If not cleared, CreatUserAsync will become CreateUser
        */
        options.RemoveActionPostfixes.Clear();

        // Custom ActionName handler
        options.GetRestFulActionName = (actionName) => actionName;

        // Specify assembly to configure all API requests as POST
        options.AddAssemblyOptions(GetType().Assembly, httpVerb: "POST");

        /*
        * Specify assembly to configure URL prefix as api
        * E.g.: http://localhost:8010/api/User/CreateUser
        */
        options.AddAssemblyOptions(GetType().Assembly, apiPreFix: "api");

        /*
        * Specify assembly to configure URL prefix as api, and all requests as POST
        * E.g.: http://localhost:8010/api/User/CreateUser
        */
        options.AddAssemblyOptions(GetType().Assembly, apiPreFix: "api", httpVerb: "POST");

        // Generate request methods ["method name prefix"] = "verb"
        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);

If no matching request method is found based on the prefix verb, HttpPost is used

NamingConvention API naming options:

  • CamelCase: camelCase naming
  • PascalCase: PascalCase naming
  • SnakeCase: snake_case naming
  • KebabCase: kebab-case naming
  • ExtensionCase: extension.case naming
  • Custom: Custom API name, set via options.GetRestFulActionName = (actionName) => actionName;

Using Dynamic Api

Add the [DynamicApi(Area = ApiConsts.AreaName)] attribute to the service and inherit the IDynamicApi interface to automatically generate dynamic Api

cs
/// <summary>
/// Module service
/// </summary>
[DynamicApi(Area = ApiConsts.AreaName)]
public class ModuleService : BaseService, IModuleService, IDynamicApi
{
    /// <summary>
    /// Get page
    /// </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;
    }
}

Note

ApiConsts.AreaName defines app in the path /api/app/module/get

Dynamic Api generates request methods based on prefix verbs. Use the [HttpPost] attribute to change the request method.

New API documentation URL: http://localhost:8010/admin/index.html

Swagger API documentation URL: http://localhost:8010/admin/swagger/index.html

If the service is a dynamic Api, methods with transactions must be defined as virtual for transaction interception to work properly.

Disable Dynamic Api

Add the [NonAction] attribute to the service method to disable dynamic Api generation, making it a public service method

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

Dynamic Api Rename

Add the following attribute to the service method to rename it

API paths must use absolute paths for renaming to work properly

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

Dynamic Api Ordering

Add the [Order] attribute to the service for ordering

Smaller values appear first

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

}

Add the [Order] attribute to the service method for ordering

Larger values appear first

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

Dynamic Api Multiple Groups

Add the [DynamicApi] attribute to the service to specify different groups

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

}

Add the [ApiGroup] attribute to the service method to specify different groups

API not grouped: [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);
}

Dynamic Api Return Raw Data

When you don't want to return the unified wrapped data format

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

Add the [NonFormatResult] attribute to the service method to disable the unified data wrapper and return only the raw data

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

Dynamic Api Authorization 8.3.0

Add the [ApiAccess("permission_code")] attribute to the service method

Note

[ApiAccess("permission_code_1", "permission_code_2", All = true)]

One or more permission codes can be set

All defaults to false - access is granted if any permission code is met. Set to true to require all permission codes.

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

Exclude from Swagger Documentation

When you have API endpoints that should not be publicly exposed or displayed in the API documentation, add the [ApiExplorerSettings(IgnoreApi = true)] attribute to the method.

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

Disable Operation Logging

When you have API endpoints that should not record operation logs, add [NoOperationLog] to the method.

cs
[NoOperationLog]
public async Task GetDataListAsync()
{
    ...
}