跳到主要内容
版本:3.1

ApiControllerBase

ApiControllerBase 是 Aegis Web API 的基础控制器。可以把它理解成:帮你把 Controller 的返回格式统一起来。

它解决什么问题

有了它之后,Controller 不用再自己拼响应对象。平时最常用的也就是这几种情况:

  • 返回单对象
  • 返回列表
  • 返回分页列表
  • 返回失败消息
  • 返回参数校验失败
  • ServiceResult<T> 直接转换成标准响应
  • 文件下载

响应类型怎么理解

平时先记这三种就够:

  • ApiResponse
  • EntityResponse<T>
  • EntitiesResponse<T>

ApiResponse

基础响应类型,仅包含公共字段:

字段类型说明
Codeint状态码,200 表示成功
MessageTypeMessageType前端消息展示方式
Messagestring提示消息

它本身不带 Result,只是公共父级类型。

EntityResponse<T>

单对象返回时使用,继承 ApiResponse,额外带一个字段:

Result        → T(业务数据)

JSON 示例:

{
"code": 200,
"messageType": 40,
"message": "OK",
"result": { "id": 1, "name": "张三" }
}

EntitiesResponse<T>

列表或分页列表返回时使用,继承 ApiResponseResult 内包含列表和分页信息:

Result.List         → T(列表数据)
Result.Total → long(总条数)
Result.CurrentIndex → int(当前页码,为 0 时不输出到 JSON)

JSON 示例:

{
"code": 200,
"messageType": 40,
"message": "OK",
"result": {
"list": [{ "id": 1 }, { "id": 2 }],
"total": 100,
"currentIndex": 1
}
}

MessageType 枚举

MessageType 控制前端如何展示消息:

名称含义
10Notice提醒
20Message消息框
30Confirm确认框
40Ignore忽略,前端不展示(默认值)
100Fallback降级

大部分场景用 Ignore(不需要前端弹消息)或 Notice(需要前端弹出提示)就够了。

ResponseCode 枚举

Code 字段的常用取值:

名称含义
200Success成功
201Tips成功,但带提示信息
401UnAuthenticate未认证
402TokenExpiredToken 已过期
405NotAllowed不允许执行
100Failed失败
105ParametersWrong参数验证失败

成功系列方法

SuccessResult() — 无数据成功

不携带业务数据,仅返回成功状态。

[HttpDelete("Delete")]
public async Task<ApiResponse> Delete(long id)
{
await _userService.DeleteAsync(id);
return SuccessResult();
}

返回:ApiResponse,Code=200,Message="OK"。

SuccessResult(entity) — 返回单对象

[HttpGet("GetUser")]
public async Task<ApiResponse> GetUser(long id)
{
var dto = await _userService.GetUserAsync(id);
return SuccessResult(dto);
}

返回:EntityResponse<T>,Code=200。

SuccessResult(message, entity, messageType) — 带消息的单对象

当你需要前端展示提示信息时使用:

return SuccessResult("保存成功", dto, MessageType.Notice);

SuccessListResult(list) — 返回列表

不带分页信息的列表:

[HttpGet("GetAllRoles")]
public async Task<ApiResponse> GetAllRoles()
{
var list = await _roleService.GetAllAsync();
return SuccessListResult(list);
}

SuccessListResult(list, current, total) — 返回分页列表

手动指定页码和总数:

[HttpGet("GetList")]
public async Task<ApiResponse> GetList([FromQuery] PageListRequest request)
{
var list = await _userService.GetUsersAsync(request);
return SuccessListResult(list, request.PageIndex, totalCount);
}

SuccessListResult(pagedResult) — 接收 PagedResult

如果 Service 层返回的是 PagedResult<T>,可以直接传入:

[HttpGet("GetPagedList")]
public async Task<ApiResponse> GetPagedList([FromQuery] PageListRequest request)
{
var pagedResult = await _userService.GetPagedUsersAsync(request);
return SuccessListResult(pagedResult);
}

SuccessListResult(message, list, messageType) — 带消息的列表

return SuccessListResult("查询成功,数据已更新", list, MessageType.Notice);

失败系列方法

FailedResult(message) — 简单失败

最常用的失败返回:

if (dto == null)
{
return FailedResult("未查询到相关数据");
}

默认 Code=100,MessageType=Notice。

FailedResult(messages) — 多条失败消息

参数校验有多条错误时使用,消息之间会以换行连接:

var errors = new[] { "姓名不能为空", "年龄超出范围" };
return FailedResult(errors);

FailedResult(code, message) — 自定义 Code 的失败

需要使用特定错误码时:

// 使用 int 数字 Code
return FailedResult(500, "系统内部错误");

FailedResult(ResponseCode, message) — 使用 ResponseCode 枚举

// 使用预定义枚举
return FailedResult(ResponseCode.NotAllowed, "当前用户无权执行此操作");
return FailedResult(ResponseCode.TokenExpired, "登录已过期,请重新登录");

FailedResult(message, entity) — 携带数据的失败

失败时同时返回部分数据,前端可据此做后续处理:

return FailedResult("部分数据导入失败,请检查标红行", failedRows);

InValidResult(message) — 参数验证失败

专门用于参数校验不通过的场景,Code=105:

if (request.Id <= 0)
{
return InValidResult("Id 必须大于 0");
}

通用方法

Result(code, message, entity, messageType) — 通用单实体响应

当你需要完全控制 Code 和消息类型时:

return Result(ResponseCode.Tips, "操作成功但存在警告", result, MessageType.Notice);

返回 EntityResponse<T>

ListResult(code, message, entities, total, currentIndex, messageType) — 通用列表响应

完全控制列表响应的所有字段:

return ListResult(ResponseCode.Success, "查询成功", list, total: 100, currentIndex: 1);

返回 EntitiesResponse<T>

ServiceResult 转换

CallResult(serviceResult) — 将 ServiceResult 转为响应

如果 Service 层使用了 ServiceResult<T>,Controller 里不需要手动判断成功/失败:

[HttpPost("Create")]
public async Task<ApiResponse> Create(CreateRequest request)
{
var result = await _userService.CreateAsync(request);
return CallResult(result);
}

CallResult 会根据 ServiceResult.IsSuccess 自动选择返回成功还是失败:

  • IsSuccess = true → 调用 SuccessResult(data, message, messageType)
  • IsSuccess = false → 调用 FailedResult(message, messageType)

文件下载

FileResult(bytes, fileType, fileName) — 按文件类型下载

传入 FileType 枚举,自动识别 Content-Type:

[HttpGet("ExportExcel")]
public async Task<ActionResult> ExportExcel()
{
var bytes = await _reportService.GenerateExcelAsync();
return FileResult(bytes, FileType.xlsx, "报表.xlsx");
}

FileType 支持的类型:

枚举值说明
xls / xlsxExcel
doc / docxWord
ppt / pptxPowerPoint
pdfPDF
zipZIP 压缩包

FileResult(bytes, contentType, fileName) — 自定义 Content-Type

return FileResult(bytes, "application/octet-stream", "data.bin");

FileResult(stream, contentType, fileName) — 流式下载

适合大文件场景:

[HttpGet("DownloadTemplate")]
public ActionResult DownloadTemplate()
{
var stream = new FileStream(templatePath, FileMode.Open);
return FileResult(stream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "模板.xlsx");
}

方法速查表

场景方法返回类型
无数据成功SuccessResult()ApiResponse
返回单对象SuccessResult(entity)EntityResponse<T>
带消息的单对象SuccessResult(message, entity, messageType)EntityResponse<T>
返回列表SuccessListResult(list)EntitiesResponse<T>
返回分页列表SuccessListResult(list, current, total)EntitiesResponse<T>
接收 PagedResultSuccessListResult(pagedResult)EntitiesResponse<T>
带消息的列表SuccessListResult(message, list, messageType)EntitiesResponse<T>
简单失败FailedResult(message)ApiResponse
多条失败消息FailedResult(messages)ApiResponse
自定义 Code 失败FailedResult(code, message)ApiResponse
枚举 Code 失败FailedResult(ResponseCode, message)ApiResponse
携带数据的失败FailedResult(message, entity)ApiResponse
参数验证失败InValidResult(message)ApiResponse
ServiceResult 转换CallResult(serviceResult)ApiResponse
通用单实体Result(code, message, entity, messageType)EntityResponse<T>
通用列表ListResult(...)EntitiesResponse<T>
文件下载(枚举类型)FileResult(bytes, FileType, fileName)ActionResult
文件下载(自定义类型)FileResult(bytes, contentType, fileName)ActionResult
文件下载(流)FileResult(stream, contentType, fileName)ActionResult

如何扩展 Response

当标准的三种响应类型(ApiResponseEntityResponse<T>EntitiesResponse<T>)不满足业务需求时,有以下几种扩展方式。

方式一:继承 ApiResponse 添加自定义字段

ApiResponserecord 类型,可以被继承。如果你的业务需要固定的响应结构但又需要额外字段,可以定义自己的响应类型。

using Aegis.Transfer.Responses;

// 扩展:带 TraceId 的响应
public record TraceableResponse : ApiResponse
{
public string TraceId { get; set; }
public object Result { get; set; }
}

在 Controller 中直接使用:

[HttpGet("GetWithTrace")]
public ApiResponse GetWithTrace(long id)
{
var dto = _userService.GetUser(id);
return new TraceableResponse
{
Code = 200,
Message = "OK",
TraceId = HttpContext.TraceIdentifier,
Result = dto
};
}

输出 JSON:

{
"code": 200,
"messageType": 0,
"message": "OK",
"traceId": "00-abc123-def456-01",
"result": { "id": 1, "name": "张三" }
}

方式二:直接构造 EntityResponse / EntitiesResponse

基类方法(SuccessResultSuccessListResult 等)本质上也是在构造 EntityResponse<T>EntitiesResponse<T>。如果需要完全控制输出,可以跳过基类方法直接构造。

[HttpGet("GetDetail")]
public ApiResponse GetDetail(long id)
{
var dto = _userService.GetDetail(id);

return new EntityResponse<UserDto>
{
Code = (int)ResponseCode.Tips,
Message = "查询成功,请注意账户状态变更",
MessageType = MessageType.Notice,
Result = dto
};
}

列表场景同理:

[HttpGet("GetList")]
public ApiResponse GetList([FromQuery] PageListRequest request)
{
var (list, total) = _userService.GetList(request);

return new EntitiesResponse<List<UserDto>>
{
Code = 200,
Message = "OK",
Result = new EntitiesResult<List<UserDto>>
{
List = list,
Total = total,
CurrentIndex = request.PageIndex
}
};
}

方式三:封装项目级基类方法

如果项目中有大量重复的自定义响应逻辑,推荐封装一个项目专属的 Controller 基类:

using Aegis.Core.Infrastructure.Controller;
using Aegis.Transfer.Enumerates;
using Aegis.Transfer.Responses;

public class MyAppControllerBase : ApiControllerBase
{
/// <summary>
/// 返回带 TraceId 的成功结果
/// </summary>
protected ApiResponse SuccessWithTrace<T>(T entity)
{
return new EntityResponse<T>
{
Code = 200,
Message = "OK",
Result = entity
};
}

/// <summary>
/// 返回带额外统计信息的列表
/// </summary>
protected ApiResponse SuccessListWithSummary<T>(
T list, long total, string summary) where T : class, IEnumerable, new()
{
return ListResult(ResponseCode.Success, summary, list, total);
}
}

业务 Controller 继承这个基类即可:

[ApiController]
[Route("api/[controller]")]
public class OrderController : MyAppControllerBase
{
[HttpGet("GetOrder")]
public async Task<ApiResponse> GetOrder(long id)
{
var order = await _orderService.GetAsync(id);
return SuccessWithTrace(order);
}
}

什么时候该扩展

场景推荐做法
只需要返回单对象/列表/失败直接用基类方法
需要在响应中加固定字段(如 TraceId、ServerTime)继承 ApiResponse 定义自定义类型
某些接口需要特殊 Code 或 MessageType直接构造 EntityResponse<T>
多个 Controller 都有相同的自定义返回逻辑封装项目级 ControllerBase

扩展时注意

  • 所有响应类型最终都继承 ApiResponse,Controller 方法签名统一返回 Task<ApiResponse> 即可兼容
  • EntitiesResult<T>CurrentIndex 默认值为 0,序列化时会被忽略(JsonIgnore(Condition = WhenWritingDefault)
  • 如果前端依赖固定的 JSON 结构,新增字段不要放在 Result 里面改,而是直接在自定义响应类型的同级添加

实际项目里怎么用最省事

在业务项目里,最省事的写法通常是:

  • Controller 统一返回 Task<ApiResponse>
  • 单对象使用 SuccessResult
  • 列表使用 SuccessListResult
  • 失败使用 FailedResult
  • 复杂 Service 分支可考虑 CallResult
  • 文件导出使用 FileResult

这样前后端都比较好对齐,Swagger 里也更稳定。

这里要特别记一下

当前 Aegis 的基础响应类型是 ApiResponse / EntityResponse<T> / EntitiesResponse<T>。如果你在其他规范材料里看到 ApiResult<T> 之类的写法,请先把它视为参考口径,不要直接覆盖当前代码事实。