关于c#:AspNET-通过实现-IHttpModule-来修改-Http-POST-请求的请求体

6次阅读

共计 3662 个字符,预计需要花费 10 分钟才能阅读完成。

需要起因

在 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);
    }
}
正文完
 0