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

使用OWIN中间件修改ASP.NET WebAPI2请求体后接口仍取原始内容如何解决

问题原因

  • 替换请求体为新的MemoryStream后,流的Position指针停留在写入后的末尾位置,后续WebAPI读取请求体时从末尾开始读,无法读取到清理后的内容,会 fallback 到之前缓存的原始请求数据。
  • 没有同步更新请求头的Content-Length值,清理后的内容长度和原始内容长度不一致,WebAPI会按照原始长度读取内容,出现读取异常或拿到原始数据的问题。
  • 标记为async的Sanitize方法内部没有任何await操作,实际为同步执行,可能引发线程上下文异常,同时原始请求流没有正确释放,部分场景会触发流锁。
  • IIS托管场景下,ASP.NET原生管道会提前缓存请求体,OWIN层修改的Body没有同步到ASP.NET内部的请求对象缓存,导致WebAPI依然读取原始数据。

解决方案

方案1:修复现有OWIN中间件

修改后的中间件代码如下:

public class SanitizerMiddleware : OwinMiddleware
{
    public SanitizerMiddleware(OwinMiddleware next) : base(next)
    {
    }

    public override async Task Invoke(IOwinContext context)
    {
        if (context.Request.Method == "POST" && context.Request.Body.CanRead)
        {
            // 异步读取原始请求内容
            using (var reader = new StreamReader(context.Request.Body, Encoding.UTF8))
            {
                string originalText = await reader.ReadToEndAsync();
                // 执行内容清理逻辑
                string sanitizedText = new HtmlSanitizer()
                    .Sanitize(originalText)
                    .Replace("'", "`")
                    .Replace("(", "|")
                    .Replace(")", "|");
                byte[] sanitizedBytes = Encoding.UTF8.GetBytes(sanitizedText);
                
                // 生成新的请求流,重置指针到起始位置
                var newBodyStream = new MemoryStream(sanitizedBytes);
                newBodyStream.Position = 0;
                context.Request.Body = newBodyStream;
                // 同步更新内容长度头
                context.Request.Headers["Content-Length"] = sanitizedBytes.Length.ToString();
            }
        }
        await Next.Invoke(context);
    }
}

方案2:改用WebAPI原生DelegatingHandler(更稳定,适配IIS托管场景)

如果修复中间件后仍然不生效,可改用WebAPI管道内的消息处理程序,避免OWIN和ASP.NET管道的缓存冲突:

  1. 自定义消息处理程序
public class SanitizeRequestHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        if (request.Method == HttpMethod.Post && request.Content != null)
        {
            string originalContent = await request.Content.ReadAsStringAsync();
            string sanitizedContent = new HtmlSanitizer()
                .Sanitize(originalContent)
                .Replace("'", "`")
                .Replace("(", "|")
                .Replace(")", "|");
            // 替换请求内容,保持原有内容类型
            request.Content = new StringContent(
                sanitizedContent, 
                Encoding.UTF8, 
                request.Content.Headers.ContentType?.MediaType ?? "application/json"
            );
        }
        return await base.SendAsync(request, cancellationToken);
    }
}
  1. 在WebApiConfig中注册处理程序
public static class WebApiConfig
{
    public static HttpConfiguration Register()
    {
        var config = new HttpConfiguration();
        // 注册清理处理程序,插入到处理管道最前面
        config.MessageHandlers.Insert(0, new SanitizeRequestHandler());
        
        // 保留你原来的其他WebAPI配置
        config.MapHttpAttributeRoutes();
        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
        return config;
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 07:39:04