You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 01:40:47