Asp.NET 通过实现 IHttpModule 来修改 Http POST 请求的请求体

RyougiChan

需求起因

在 Web 开发中,通常需要对前端(front-end)传到后端(back-end)的数据进行过滤和校验,以防范诸如 SQL 注入、XSS 注入之类的攻击。
在 Java 中,通过继承 HttpServletRequestWrapper 类可重新包装当前请求(Http Request)并将其替换,它的实现如下:

public class RequestWrapper extends HttpServletRequestWrapper {

    private String AMP = "&";

    private String queryString;

    public RequestWrapper(HttpServletRequest request, String queryString)
    {
        super(request);
        this.queryString = queryString;
    }

    public String getQueryString()
    {
        String query = null;
        if (super.getQueryString() != null) {
            query = super.getQueryString() + AMP + this.queryString;
        } else {
            query = this.queryString;
        }
        return query;
    }
}


public class RequestHandler{
       public void changeRequest{
                RequestWrapper reqWrapper = new RequestWrapper(httpRequest, newQueryString() );
                FilterChain.doFilter(reqWrapper, servletResponse); // this FilterChain guy helps to replace the old request to  
                                                                                          // new request to runtime
       }
}

然而,Asp.NET 中并没有类似的包装器实现,通常的做法是在每一个请求通过(ProcessRequest)后再进行合法性校验,它导致的问题是每个请求 Action 都需要重复调用过滤方法来过滤请求参数。这显然冗余且麻烦得不太合理。

Asp.NET 中的一种可行实现思路

IIS 7.0+ 中,可以通过 HttpModule 来实现。

前置知识

下面列举了一些可能需要提前了解的知识点

步骤

下面以在 .Net MVC 中配置示例

1. 配置

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  // ignore other configuration
  <system.webServer>
    <validation validateIntegratedModeConfiguration="false" />
    <modules runAllManagedModulesForAllRequests="true">
       <add name="httpModule" type="HttpRequestFilterModule" />
        </modules>
    </system.webServer>
</configuration>

2. 实现 HttpRequestFilterModule

using MvcApplication1.Filter;
using System.Web;

public class HttpRequestFilterModule : IHttpModule
{
    public void Dispose()
    {
        throw new NotImplementedException();
    }

    public void Init(HttpApplication context)
    {
        context.BeginRequest += new EventHandler(Application_BeginRequest);
    }

    public void Application_BeginRequest(object sender, EventArgs args)
    {
        HttpContext context = HttpContext.Current;
        HttpRequest request = context.Request;

        // 仅过滤 POST 请求
        if (context.Request.HttpMethod != "POST")
        {
            return;
        }

        var requestCallback = new Func<string, string>(content => {
            // may modify request content here
            return "The request content is modified.";
        });
        context.Request.Filter = new RequestFilter(context.Request.Filter, context.Request.ContentEncoding, requestCallback);
    }
}

RequestFilter.cs

using System;
using System.IO;
using System.Text;

namespace MvcApplication1.Filter
{
    public class RequestFilter : Stream
    {
        //临时缓冲区用于优化加载
        private MemoryStream ms;

        //处理原始输出流
        private Stream _stream;

        //响应的编码方式
        private Encoding _encoding;

        //回调delegate
        private Func<string, string> _callback;

        public RequestFilter(Stream stream, Encoding encoding, Func<string, string> callback)
        {
            _stream = stream;
            _encoding = encoding;
            _callback = callback;
        }

        public override void Flush()
        {
            ms.Flush();
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            return ms.Seek(offset, origin);
        }

        public override void SetLength(long value)
        {
            ms.SetLength(value);
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            if (ms == null)
            {
                var sr = new StreamReader(_stream, _encoding);
                string content = sr.ReadToEnd();

                //回调执行
                content = _callback(content);

                byte[] bytes = _encoding.GetBytes(content);
                ms = new MemoryStream();
                ms.Write(bytes, 0, bytes.Length);
                ms.Seek(0, SeekOrigin.Begin);
            }

            return ms.Read(buffer, offset, count);
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            throw new NotSupportedException();
        }

        public override bool CanRead
        {
            get { return true; }
        }

        public override bool CanSeek
        {
            get { return false; }
        }

        public override bool CanWrite
        {
            get { return false; }
        }

        public override long Length
        {
            get { return ms.Length; }
        }

        public override long Position
        {
            get { return ms.Position; }
            set { throw new NotSupportedException(); }
        }
    }
}

3. 验证结果

如果正常工作,访问下面 Get Action, 将得到 The request content is modified. 的输出结果。
public class HomeController : Controller
{
    [HttpPost]
    public ActionResult Get()
    {

        string requestBody = string.Empty;

        using (StreamReader reader = new StreamReader(Request.InputStream))
        {
            requestBody = reader.ReadToEnd();
        }

        return Content(requestBody);
    }
}
阅读 614
1 声望
0 粉丝
0 条评论
你知道吗?

1 声望
0 粉丝
宣传栏