如何修改HttpContext.Response?统一API返回格式实现求助
实现API统一响应格式(成功场景)
一、定义统一响应模型
先创建和需求格式匹配的通用响应模型,和你已有的错误模型保持一致:
// 统一响应模型 public class ResultModel<T> { public T Body { get; set; } public ErrorModel Error { get; set; } } // 错误模型(和你现有失败逻辑中的模型一致) public class ErrorModel { public string Message { get; set; } public string StatusCode { get; set; } }
二、方案1:用中间件拦截包装成功响应
通过中间件拦截控制器返回的成功响应,将其包装成统一格式:
1. 实现中间件
public class UnifiedResponseMiddleware { private readonly RequestDelegate _next; private readonly JsonSerializerOptions _jsonOptions; public UnifiedResponseMiddleware(RequestDelegate next, JsonSerializerOptions jsonOptions) { _next = next; _jsonOptions = jsonOptions; } public async Task InvokeAsync(HttpContext context) { // 保存原始响应流,替换为内存流以便读取响应内容 var originalResponseBody = context.Response.Body; using var newResponseBody = new MemoryStream(); context.Response.Body = newResponseBody; try { await _next(context); // 仅处理2xx状态码的成功响应 if (context.Response.StatusCode is >= 200 and < 300) { // 重置内存流指针到起始位置 newResponseBody.Seek(0, SeekOrigin.Begin); var originalContent = await new StreamReader(newResponseBody).ReadToEndAsync(); // 反序列化原始响应内容 var originalModel = JsonSerializer.Deserialize<dynamic>(originalContent, _jsonOptions); // 包装成统一格式 var unifiedResult = new ResultModel<dynamic> { Body = originalModel, Error = null }; // 重置响应流,写入统一格式内容 context.Response.Body = originalResponseBody; context.Response.ContentType = "application/json"; await JsonSerializer.SerializeAsync(context.Response.Body, unifiedResult, _jsonOptions); } else { // 非成功响应,直接将内存流内容复制回原始响应流 newResponseBody.Seek(0, SeekOrigin.Begin); await newResponseBody.CopyToAsync(originalResponseBody); } } finally { context.Response.Body = originalResponseBody; } } } // 扩展方法,方便注册中间件 public static class UnifiedResponseMiddlewareExtensions { public static IApplicationBuilder UseUnifiedResponse(this IApplicationBuilder app) { return app.UseMiddleware<UnifiedResponseMiddleware>(); } }
2. 注册中间件
在Program.cs中添加中间件注册,注意顺序要放在路由和端点之间:
var builder = WebApplication.CreateBuilder(args); // 添加控制器服务 builder.Services.AddControllers(); // 配置Json序列化选项(和你现有代码保持一致,比如驼峰命名) builder.Services.AddControllers().AddJsonOptions(options => { options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; }); // 注册Json序列化选项为单例,供中间件使用 builder.Services.AddSingleton(sp => sp.GetRequiredService<IOptions<JsonOptions>>().Value.JsonSerializerOptions); var app = builder.Build(); app.UseHttpsRedirection(); app.UseRouting(); app.UseAuthorization(); // 注册统一响应中间件 app.UseUnifiedResponse(); app.MapControllers(); app.Run();
三、方案2:用Action过滤器精准处理控制器响应
如果只需要处理控制器的Action结果,用Action过滤器更简洁:
1. 实现过滤器
public class UnifiedResponseFilter : IAsyncResultFilter { private readonly JsonSerializerOptions _jsonOptions; public UnifiedResponseFilter(JsonSerializerOptions jsonOptions) { _jsonOptions = jsonOptions; } public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) { // 仅处理成功的ObjectResult(控制器返回对象时默认生成的结果类型) if (context.Result is ObjectResult objectResult && objectResult.StatusCode is >= 200 and < 300) { // 包装成统一格式,替换原结果 var unifiedResult = new ResultModel<object> { Body = objectResult.Value, Error = null }; context.Result = new JsonResult(unifiedResult, _jsonOptions); } await next(); } }
2. 注册过滤器
在Program.cs中添加过滤器注册:
builder.Services.AddControllers(options => { // 添加全局过滤器,对所有控制器生效 options.Filters.Add<UnifiedResponseFilter>(); });
注意事项
- 确保Json序列化选项和你现有失败逻辑中的配置一致(比如驼峰命名),避免格式不统一。
- 如果控制器返回的是
ContentResult、FileResult等非对象结果,需要在中间件/过滤器中额外处理。 - 失败场景你已经实现,上述逻辑仅处理2xx状态码的成功响应,不会覆盖你已有的失败处理逻辑。
内容的提问来源于stack exchange,提问作者Ttshkalol
相关产品推荐
相关产品推荐

