使用 HttpRequest.Filter 来过滤或转换 Http 输入流

来源:互联网 发布:安畅网络工作怎么样 编辑:程序博客网 时间:2024/06/13 14:31

 

HttpRequest.Filter 属性为我们提供了一个过滤和转换器,可以在进行页面处理之前对 Http 输入流数据进行更改。如果指定了该属性,所有 Http 的请求数据都会通过你所指定的过滤器,这意味着可以在数据到达页面之前进行筛选或转换,并且对于所有的页面,只需做一个统一的处理,而不必每个页面都去增加代码。常见的应用有非法字符过滤、繁简转换等。

 

其实所谓的过滤器,说白了就是一个字符流,而过滤或转换,就是对字符流中的数据进行相应的修改。所以要实现自己的过滤器,必须先继承 Stream 类,具体的实现可以参见 MSDN à System.Web.HttpRequest à Filter 属性的样例。

 

这里需要指出 MSDN 样例的 Read() 方法,数据的过滤及转换必须在该方法中实现,但 MSDN 的样例有误导读者之嫌。MSDN 的样例代码如下:

 

  1. public override int Read(byte[] buffer, int offset, int count)
  2. {
  3.     int c = _sink.Read(buffer, offset, count);
  4.         for (int i = 0; i < count; i++)
  5.         {
  6.             if (buffer[offset+i] >= 'a' && buffer[offset+i] <= 'z')
  7.                 buffer[offset+i] -= ('a'-'A');
  8.         }
  9.         return c;
  10. }

这个实现存在一个很大的问题,对于 Http 请求,发回到服务器的数据(POST方式)类似于:

 

__VIEWSTATE=%2FwEPDwUKLTc0NjAyOTQ3NQ9kFgICAw9kFgICAw8QDxYGHg1EYXRhVGV4dEZpZWxkBQV2YWx1ZR4ORGF0YVZhbHVlRmllbGQFA2tleR4LXyFEYXRhQm91bmRnZBAVBAFhAWIBYwFkFQQBMQEyATMBNBQrAwRnZ2dnZGRkIDCyaL%2FY%2FW7ReT14C10c%2FAyHzzA%3D&txtInput=ddssaa&ddlXml=1&__EVENTVALIDATION=%2FwEWBgLpsvzICwKT8abSBwKErvPrCQKFrvPrCQKGrvPrCQKHrvPrCcf8Jy04aj7zl8ulALFDIJBFFLcM

 

可以看出这些数据由多组名值对组成,通过 ’&’ 分隔,我使用红色将名称标识了出来,等号后的编码字符串为名称对应的值。在通常的代码中,我们可以使用Request.Form[名称] 获取到相应的值(已解码),其实页面控件中的 ID 也会在这里表示为名称。这样一来,按照 MSDN 的样例,必然会将名称替换为其它的值,到后期页面代码处理时,就会因控件 ID 与请求数据中的名称不一致而报错。

 

在实际应用中,一般需要转换或过滤的都是控件中的值,所以只要提取控件的值进行修改就可以了,最简单的方法是用正则表达式,一个简单的示例:

  1.     public override int Read(byte[] buffer, int offset, int count)
  2.     {
  3.         int len = _sink.Read(buffer, offset, count);
  4.         if (len == 0)
  5.         {
  6.             Array.Clear(buffer, 0, count);
  7.             return len;
  8.         }
  9.         System.Text.Encoding curEncoding = HttpContext.Current.Request.ContentEncoding;
  10.         string strBuff = curEncoding.GetString(buffer);             //__VIEWSTATE 和 __EVENTVALIDATION 由 ASP.NET 使用,它们分别置于头部和尾部,所以不用考虑。
  11.         System.Text.RegularExpressions.Regex re = new System.Text.RegularExpressions.Regex(@"&/w+=([/w@/-.*+]+)&");
  12.         strBuff = re.Replace(strBuff, new System.Text.RegularExpressions.MatchEvaluator(ReplaceMatch));
  13.         Array.Clear(buffer, 0, count);
  14.         byte[] newBuff = curEncoding.GetBytes(strBuff);
  15.         newBuff.CopyTo(buffer, 0);      
  16.         return len;
  17.     }

注意返回值,表示读取到的字符串长度,这个值不能修改,并且过滤或转换后字符数据流的长度必须和原始数据一致(注意:是编码后的字符长度,如’<’,编码后为’%3C’,是三个字长,所以不能替换为’A’),否则会报错。

 

用于实际替换的方法(上面程序 re.Replace 中所使用的委托)

 

  1. private string ReplaceMatch(System.Text.RegularExpressions.Match match)
  2.     {
  3.         string prefixStr = match.ToString().Split('=')[0];
  4.         string subMatchStr = HttpContext.Current.Server.UrlDecode(match.Groups[1].ToString());
  5.         System.Text.RegularExpressions.Regex re = new System.Text.RegularExpressions.Regex(@"string");
  6.         subMatchStr = HttpContext.Current.Server.UrlEncode(re.Replace(subMatchStr, "ABCDEF"));//注意:替换前后的字符数编码后必须保持一致
  7.         return prefixStr + "=" + subMatchStr + "&";
  8.     } 

过滤器的使用很简单,在 Global.asax 中,增加

  1.     public void Application_BeginRequest(object sender, EventArgs e)
  2.     {
  3.         Request.Filter = new inputFilter(Request.Filter);       
  4.     }

inputFilter 是自定义的过滤器。

 

BTW,如果仅是对输入流进行检测,若发现非法字符,则转向错误页面,只需在Application_BeginRequest 事件中检测 Request.Form 集合中的值就可以了,没必要用过滤器这么麻烦。

原创粉丝点击