如何在.NET Core 6中间件中修改默认响应状态码
问题描述
我开发了一个.NET Core 6 API项目,采用分层架构,所有数据操作和异常处理都在服务层完成。服务层统一返回CurrentResponse对象,包含三个成员:
- Data:响应正常时返回数据,异常时为null
- Status:实际的HTTP响应状态码
- Message:自定义提示信息
目前的问题是,控制器始终返回状态码200,而真实的状态码存在于CurrentResponse对象中,移动端开发需要额外解析判断,非常不便。不想逐个修改控制器代码,希望通过响应包装中间件自动读取CurrentResponse并设置对应的HTTP响应状态码。
附上相关代码:
控制器代码
[HttpPost] [Route("login")] [AllowAnonymous] public IActionResult Login([FromBody] LoginVM loginVM) { loginVM.Password = loginVM.Password.Encrypt(); CurrentResponse response = _accountService.GetValidUser(loginVM); User user = (User)(response.Data); if (user != null) { response = GetDetails(response, user, loginVM.TimeZone); return Ok(response); } return Ok(response); }
服务层代码
public CurrentResponse GetValidUser(LoginVM loginVM) { try { User user = _accountRepository.GetValidUser(loginVM.Email, loginVM.Password); if (user == null) { CreateResponse(null, HttpStatusCode.NotFound, "Invalid Credentials"); return _currentResponse; } UserVSCompany userVSCompany = _userVSCompanyRepository.GetDefaultCompanyByUserId(user.Id); string companyId = "0"; if(userVSCompany != null) { companyId = userVSCompany.CompanyId.ToString(); user.CompanyId = userVSCompany.CompanyId; } user.ImageName = $"{Configuration.ConfigurationSettings.Instance.UploadDirectoryPath}/{UploadDirectories.UserProfileImage}/{companyId}/{user.ImageName}"; Company company = _companyRepository.FindByCondition(p => p.Id == user.CompanyId); if(company != null) { if(!company.IsActive) { CreateResponse(null, HttpStatusCode.NotFound, "Your organisation is not activated"); return _currentResponse; } if(company.IsDeleted) { CreateResponse(null, HttpStatusCode.NotFound, "Organization has been deleted"); return _currentResponse; } } if (!user.IsActive) { CreateResponse(null, HttpStatusCode.NotFound, "Your account is not activated"); } else if (user.IsDeleted) { CreateResponse(null, HttpStatusCode.NotFound, "Your account has been deleted"); } else { if (company != null) { user.CompanyName = company.Name; } var userRoleDetails = _userRoleRepository.FindByUserIdAndCompanyId(user.Id, user.CompanyId); user.RoleName = userRoleDetails.Name; user.RoleId = userRoleDetails.Id; CreateResponse(user, HttpStatusCode.OK, "User is valid"); } return _currentResponse; } catch (Exception exc) { CreateResponse(null, HttpStatusCode.InternalServerError, exc.ToString()); return _currentResponse; } }
解决方案
可以通过以下步骤创建中间件来自动处理响应状态码:
1. 创建响应包装中间件
新建一个中间件类,拦截响应内容,解析CurrentResponse对象并设置对应的HTTP状态码:
public class ResponseStatusCodeMiddleware { private readonly RequestDelegate _next; private readonly ILogger<ResponseStatusCodeMiddleware> _logger; public ResponseStatusCodeMiddleware(RequestDelegate next, ILogger<ResponseStatusCodeMiddleware> logger) { _next = next; _logger = logger; } public async Task InvokeAsync(HttpContext context) { // 保存原始响应流,后续替换为修改后的内容 var originalBodyStream = context.Response.Body; using var responseBody = new MemoryStream(); context.Response.Body = responseBody; try { // 调用下一个中间件 await _next(context); // 仅处理成功返回的响应(状态码200的情况) if (context.Response.StatusCode == StatusCodes.Status200OK) { // 重置流位置,读取响应内容 responseBody.Seek(0, SeekOrigin.Begin); var responseContent = await new StreamReader(responseBody).ReadToEndAsync(); if (!string.IsNullOrEmpty(responseContent)) { try { // 反序列化为CurrentResponse对象 var currentResponse = JsonSerializer.Deserialize<CurrentResponse>(responseContent, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); if (currentResponse != null) { // 设置对应的HTTP状态码 context.Response.StatusCode = (int)currentResponse.Status; // 将原始响应内容写回流中(保持响应体不变,只修改状态码) responseBody.Seek(0, SeekOrigin.Begin); await responseBody.CopyToAsync(originalBodyStream); } } catch (Exception ex) { _logger.LogError(ex, "Failed to deserialize CurrentResponse"); // 反序列化失败时,将原始内容写回 responseBody.Seek(0, SeekOrigin.Begin); await responseBody.CopyToAsync(originalBodyStream); } } else { // 空响应时直接写回 responseBody.Seek(0, SeekOrigin.Begin); await responseBody.CopyToAsync(originalBodyStream); } } else { // 非200的响应直接写回原始内容 responseBody.Seek(0, SeekOrigin.Begin); await responseBody.CopyToAsync(originalBodyStream); } } finally { // 恢复原始响应流 context.Response.Body = originalBodyStream; } } }
2. 注册中间件
在Program.cs中,将中间件注册到管道中,注意要放在app.UseRouting()和app.UseEndpoints()之间,确保能拦截控制器返回的响应:
var builder = WebApplication.CreateBuilder(args); // ... 其他服务注册代码 ... var app = builder.Build(); // ... 其他中间件(如异常处理、静态文件等) ... app.UseRouting(); // 注册自定义响应状态码中间件 app.UseMiddleware<ResponseStatusCodeMiddleware>(); app.UseAuthorization(); app.MapControllers(); app.Run();
3. 注意事项
- 确保
CurrentResponse类的Status属性是HttpStatusCode枚举类型,或者能正确转换为int类型的HTTP状态码 - 中间件只处理状态码为200的响应,避免干扰其他异常中间件返回的响应(比如全局异常处理返回的500)
- 如果项目中使用了自定义的JSON序列化配置,需要在中间件的
JsonSerializer.Deserialize中匹配相同的配置,避免反序列化失败 - 建议添加日志记录,方便排查反序列化或状态码设置失败的问题
内容的提问来源于stack exchange,提问作者Vishal modi
相关产品推荐
相关产品推荐

