需要起因

在 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 来实现。

前置常识

上面列举了一些可能须要提前理解的知识点

  • HttpRequest.Filter 微软给的简略示例
  • IIS 7.0的ASP.NET应用程序生命周期概述
    为了无效地设置申请过滤器并利用它,应该在 BeginRequest 事件中进行设置,这是申请验证和 url 映射之后的第一个事件。

步骤

上面以在 .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);    }}