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

中间件中读写HTTP响应:响应流复制失败与文化设置问题

问题分析与解决方案

你的代码存在几个关键问题导致无法正确复制响应流:

核心问题点

  1. 重复调用await next();:方法末尾的第二次调用会导致请求管道被执行两次,响应流被重复处理,直接破坏正常的响应流程。
  2. 提前判断响应状态码:在执行next()之前就检查response.StatusCode == 200OK,此时Action还未执行,状态码尚未设置,这个判断完全无效。
  3. 未恢复原始响应流:替换响应流为内存流后,没有将response.Body恢复为原始流,导致后续中间件无法正确写入响应。
  4. 未处理响应头:修改响应体后,没有更新Content-Length头或启用分块传输,可能导致客户端接收的响应不完整。

修正后的代码

public class SetCultureAttribute : Attribute, IAsyncResourceFilter
{
    public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next)
    {
        // 1. 处理区域性设置逻辑
        var userManager = context.HttpContext.RequestServices.GetService<UserManager<ApplicationUser>>();
        string languagePreference = "en-US";
        string headerPreference = string.Empty;

        if (context.HttpContext.Request.Headers.ContainsKey("LanguageCode"))
        {
            headerPreference = context.HttpContext.Request.Headers["LanguageCode"].ToString();
        }

        if (!string.IsNullOrEmpty(headerPreference))
        {
            var culture = headerPreference == Language.English.ToString() ? "en-US" : "ja";
            CultureInfo.CurrentCulture = new CultureInfo(culture);
            CultureInfo.CurrentUICulture = new CultureInfo(culture);
        }
        else
        {
            string token = context.HttpContext.Request.Headers["Authorization"].ToString().Replace("Bearer ", "");
            if (!string.IsNullOrEmpty(token))
            {
                var tokenHandler = new JwtSecurityTokenHandler();
                var jwtToken = tokenHandler.ReadJwtToken(token);
                // 从Token Claims中提取用户信息并设置区域性(补充你的逻辑)
            }
            CultureInfo.CurrentCulture = new CultureInfo(languagePreference);
            CultureInfo.CurrentUICulture = new CultureInfo(languagePreference);
        }

        // 2. 处理登录API的响应拦截逻辑
        string actionName = context.ActionDescriptor.DisplayName;
        if (actionName.Contains("Userlogin"))
        {
            var originalResponseBodyStream = context.HttpContext.Response.Body;
            using (var responseBodyMemoryStream = new MemoryStream())
            {
                context.HttpContext.Response.Body = responseBodyMemoryStream;

                // 执行后续管道(Action和其他中间件)
                await next();

                // 仅当请求成功时处理响应体
                if (context.HttpContext.Response.StatusCode == StatusCodes.Status200OK)
                {
                    // 重置内存流位置到起始处
                    responseBodyMemoryStream.Seek(0, SeekOrigin.Begin);
                    var responseBody = new StreamReader(responseBodyMemoryStream).ReadToEnd();

                    // 提取UserId并更新用户语言偏好
                    dynamic jsonResponse = Newtonsoft.Json.JsonConvert.DeserializeObject(responseBody);
                    string userId = jsonResponse?.id;
                    if (!string.IsNullOrEmpty(userId) && userManager != null)
                    {
                        var user = await userManager.FindByIdAsync(userId);
                        if (user != null)
                        {
                            // 更新用户的语言偏好字段(需根据你的实体结构调整)
                            user.LanguagePreference = headerPreference ?? languagePreference;
                            await userManager.UpdateAsync(user);
                        }
                    }

                    // 将响应内容写回内存流(若修改了responseBody则执行此步)
                    responseBodyMemoryStream.Seek(0, SeekOrigin.Begin);
                    await new StreamWriter(responseBodyMemoryStream) { AutoFlush = true }.WriteAsync(responseBody);
                    responseBodyMemoryStream.Seek(0, SeekOrigin.Begin);

                    // 更新响应体长度
                    context.HttpContext.Response.ContentLength = responseBodyMemoryStream.Length;
                }

                // 将内存流内容复制回原始响应流
                responseBodyMemoryStream.Seek(0, SeekOrigin.Begin);
                await responseBodyMemoryStream.CopyToAsync(originalResponseBodyStream);

                // 恢复原始响应流
                context.HttpContext.Response.Body = originalResponseBodyStream;
            }
        }
        else
        {
            // 非登录API,直接执行后续管道
            await next();
        }
    }
}

关键优化说明

  • 移除重复的next()调用:确保整个请求管道只执行一次,避免重复处理响应。
  • 延后状态码判断:在执行next()之后再检查响应状态码,此时状态码已由Action设置完成。
  • 恢复原始响应流:处理完内存流后,必须将response.Body恢复为原始流,否则后续中间件无法正常工作。
  • 处理响应头:修改响应体后更新Content-Length头,确保客户端能正确接收完整响应。
  • 完善用户更新逻辑:补充了通过userManager更新用户语言偏好的具体代码(需根据你的实体结构调整)。

内容的提问来源于stack exchange,提问作者Amey Rane

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 23:33:20