agilelabs-fx-docs main topics/webapi.md

WebAPI

本页是 AgileLabs Framework WebAPI 的主题页,聚合返回封包、异常处理、认证授权和 Swagger 对齐约定。正式规则请优先阅读 框架规范 / WebAPI 规范

适用场景

  • ApiController 控制器。
  • 使用 EnvelopMessage、异常过滤器和 API 异常处理器的项目。
  • 需要接入 ASP.NET Core 认证授权的服务。

必须遵守

  • 标准 API 响应必须保持统一封包;对前端暴露的默认协议固定为 { data, code, msg, errMsg, tid }
  • API 过滤器同时配置 ExceptionHandlerFilterEnvelopFilterAttribute,除非接口显式跳过封包。
  • 模型验证与业务异常统一走异常处理链,不在每个 Action 手写重复 ModelState 分支和 catch(Exception) JSON 拼装。
  • 认证授权接入必须统一,要么框架集成,要么宿主显式接入,不做半自动半手工混用。
  • /fwdev/req_stat/jobs 等敏感入口必须受保护。
  • 业务可预期错误统一映射到规范化返回,不把数据库异常或系统异常堆栈直接返回给前端。

推荐做法

  • EnvelopFilterAttribute 或项目级统一封包层承接成功返回包装。
  • IApiExceptionProcessor 承接项目级异常映射。
  • 认证规则统一使用 ASP.NET Core 标准授权机制。
  • Swagger 文档对齐真实响应封包,避免文档和实际返回不一致。
  • 开放接口、回调接口和健康检查接口单独标明匿名访问策略。

源码入口

  • AgileLabs.WebApp/AspNet/WebApis/Filters/EnvelopFilterAttribute.cs:成功响应统一封包。
  • AgileLabs.WebApp/AspNet/WebApis/Filters/ExceptionHandlerFilter.cs:模型校验和异常总入口。
  • AgileLabs.WebApp/AspNet/WebApis/Filters/DefaultApiExceptionProcessor.cs:把异常映射成 EnvelopMessage
  • AgileLabs.WebApp/AspNet/WebApis/Exceptions/ApiException.cs:业务错误基类。
  • AgileLabs.WebApp/AspNet/WebApis/Exceptions/ModelValidateException.cs:模型验证错误。

成功返回链路

典型的 API 成功响应不是由 Controller 自己手工包装,而是由过滤器在 Action 完成后统一封包。这样做的意义是:业务层只表达真实业务对象,前端永远拿到稳定协议。

sequenceDiagram
    participant Client as Client
    participant Controller as ApiController
    participant Action as Action
    participant Envelop as EnvelopFilterAttribute

    Client->>Controller: HTTP Request
    Controller->>Action: 执行业务逻辑
    Action-->>Controller: 返回 ObjectResult / JsonResult / EmptyResult
    Controller->>Envelop: OnActionExecuted()
    Envelop->>Envelop: 检查 IgnoreEnvelopAttribute
    Envelop->>Envelop: 检查是否为 ApiController
    Envelop-->>Client: OkObjectResult(EnvelopMessage)

最小闭环示例:

[ApiController]
[Route("api/orders")]
public class OrdersController : ControllerBase
{
    [HttpGet("{id}")]
    public async Task<OrderDto> GetAsync(Guid id)
    {
        return await _service.GetAsync(id);
    }
}

只要宿主已经挂上 EnvelopFilterAttribute,上面的 OrderDto 就会被统一转换成类似结果:

{
  "data": {
    "id": "f1f7c56b-49b8-4cc7-bc22-2ab1cf74b7ae",
    "orderNo": "SO-20260412-001"
  },
  "code": 200,
  "msg": null,
  "errMsg": null,
  "tid": "trace-id"
}

异常处理链路

ExceptionHandlerFilter 同时承担两件事:Action 执行前先把 ModelState 失败转换成 ModelValidateException;执行中如果出现异常,再交给 IApiExceptionProcessor 做统一映射。

sequenceDiagram
    participant Client as Client
    participant Filter as ExceptionHandlerFilter
    participant Action as Action
    participant Processor as IApiExceptionProcessor
    participant Result as JsonResult

    Client->>Filter: 请求进入 Action Filter
    Filter->>Filter: 检查 ModelState
    alt 模型校验失败
        Filter->>Processor: Process(ModelValidateException)
        Processor-->>Result: EnvelopMessage(code/msg/errMsg)
        Result-->>Client: 200 + 规范错误封包
    else Action 抛出业务/系统异常
        Filter->>Action: 执行 Action
        Action-->>Filter: 抛出 ApiException / DbException / Exception
        Filter->>Processor: Process(exception)
        Processor-->>Result: JsonResult(EnvelopMessage)
        Result-->>Client: 200 + 规范错误封包
    end

默认处理器的行为可以概括成这张表:

异常类型 处理器行为 对前端的结果
ModelValidateException 不输出错误日志,提取字段错误 返回参数错误封包
ApiException 不输出错误日志,保留业务码 返回业务错误封包
DbException 映射为数据库错误 返回统一数据库错误码
其他 Exception 兜底处理 返回统一 500 封包

自定义项目级处理器的最小写法:

public class ProjectApiExceptionProcessor : DefaultApiExceptionProcessor
{
    protected override EnvelopMessage ProcessException(ApiException exception, ExceptionProcessContext processContext)
    {
        return new EnvelopMessage((int)exception.Code, exception.HintMessage, exception.Message);
    }
}

认证授权接入位置

  • 请求管道阶段里,框架会在 UseRouting() 之后、UseEndpoints() 之前插入认证授权。
  • 如果 buildContext.FeatureSwitch.IsIntegrateAspNetAuthentication 为真,框架会自动执行 UseAuthentication()UseAuthorization()
  • 如果项目不走框架集成,就必须在宿主自己的管道注册里完整接入,不要出现“控制器上写了 [Authorize],但管道里没跑授权中间件”的半接入状态。

Swagger 与封包对齐点

  • Swagger 不是描述 Controller 里返回了什么对象,而是描述最终对前端暴露的协议。
  • 如果宿主已启用统一封包,Swagger 文档也必须体现封包后的结构。
  • 如果某些 Action 使用 IgnoreEnvelopAttribute,就应把它们标记成特例接口,而不是默认接口。

建议在项目文档或过滤器注册处明确这两类规则:

services.AddControllers(options =>
{
    options.Filters.Add<ExceptionHandlerFilter>();
    options.Filters.Add<EnvelopFilterAttribute>();
});

常见误接法对照

误接法 风险 正确做法
Controller 手工 return new JsonResult(...) 拼封包 不同接口字段漂移 由统一过滤器封包
Action 里 catch (Exception) 后直接返回 JSON 绕开统一异常链 抛给 ExceptionHandlerFilter
只注册 UseAuthentication() 授权不生效 认证和授权一起挂
Swagger 仍描述裸对象 前后端理解不一致 文档对齐真实封包

常见坑

  • 有的接口返回裸对象,有的接口返回封包对象。
  • 对前端有的接口返回 camelCase,有的接口返回 PascalCase
  • 只注册 UseAuthentication() 不注册 UseAuthorization()
  • 把系统异常堆栈直接返回给前端。
  • 在控制器里 catch (Exception) 后手动拼 JSON,绕开统一异常处理链。

真实用例

  • niusys-webapiEnvelopMessage、Swagger 封包适配、JWT + ApiKey。
  • gmandarin-backend:宿主层启用 IsIntegrateAspNetAuthentication = true
  • woscm:项目级异常处理器、业务错误码、TraceId 回写。

相关页面