Asp.NET 經過實現 IHttpModule 來修改 Http POST 請求的請求體

需求原由

在 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 都須要重複調用過濾方法來過濾請求參數。這顯然冗餘且麻煩得不太合理。java

Asp.NET 中的一種可行實現思路

IIS 7.0+ 中,能夠經過 HttpModule 來實現。web

前置知識

下面列舉了一些可能須要提早了解的知識點c#

步驟

下面以在 .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.csapi

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);
    }
}
相關文章
相關標籤/搜索