using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Web;
using System.Globalization;

namespace HNAS.OA.OAWebApp
{
/// <summary>
/// 以Post方式提交的变量的集合。
/// </summary>
/// <remarks>
/// 不包含提交的文件。
/// </remarks>
internal class PostVariableCollection : NameValueCollection
{
/// <summary>
/// Content Type
/// </summary>
private string contentType = string.Empty;

/// <summary>
/// 分界符
/// </summary>
private byte[] boundary;

/// <summary>
/// 初始化类 PostVariableCollection 的一个新实例
/// </summary>
public PostVariableCollection()
{
FillFormStream(Encoding.Default);
}

/// <summary>
/// 初始化类 PostVariableCollection 的一个新实例
/// </summary>
/// <param name="isUrlDecode">是否进行Url解码</param>
/// <param name="encoding">编码类型</param>
public PostVariableCollection(Encoding encoding)
{
FillFormStream(encoding);
}

/// <summary>
/// 使用HTTP实体主体内容填充集合
/// </summary>
/// <param name="isUrlDecode"></param>
/// <param name="encoding"></param>
private void FillFormStream(Encoding encoding)
{
contentType = HttpContext.Current.Request.ContentType;

if (!string.IsNullOrEmpty(contentType))
{
System.IO.Stream entityStream = HttpContext.Current.Request.InputStream;

// 获取HTTP实体主体的内容
byte[] bytes = GetEntityBody(entityStream, 0);

if (bytes == null || bytes.Length <= 0)
{
return;
}

// 因为是字节数据,所有的数据都需要解码
if (contentType.StartsWith("application/x-www-form-urlencoded", System.StringComparison.CurrentCultureIgnoreCase))
{
try
{
FillFromBytes(bytes, encoding);
return;
}
catch (Exception ex)
{
throw new HttpException("Invalid_urlencoded_form_data", ex);
}
}

if (contentType.StartsWith("multipart/form-data", System.StringComparison.CurrentCultureIgnoreCase))
{
if (GetMultipartBoundary())
{
try
{
// 获取各个参数
FillFromMultipartBytes(bytes, encoding);
return;
}
catch (Exception ex)
{
throw new HttpException("Invalid_multipart_form_data", ex);
}
}
}
}
}

/// <summary>
/// 从字节数组读取变量填充到集合
/// </summary>
/// <param name="bytes"></param>
/// <param name="encoding"></param>
private void FillFromBytes(byte[] bytes, Encoding encoding)
{
// 字节数组长度
int bLen = (bytes != null) ? bytes.Length : 0;

// 遍历字节数组
for (int i = 0; i < bLen; i++)
{
string parameterName;
string parameterValue;

//参数名开始位置
int startIndex = i;

//参数名结束位置
int endIndex = -1;

while (i < bLen)
{
byte bt = bytes[i];

// 符号:=
if (bt == 0x3d)
{
if (endIndex < 0)
{
endIndex = i;
}
}
else if (bt == 0x26) //符号:&
{
break;
}
i++;
}

if (endIndex >= 0)
{
parameterName = HttpUtility.UrlDecode(bytes, startIndex, endIndex - startIndex, encoding);
parameterValue = HttpUtility.UrlDecode(bytes, endIndex + 1, (i - endIndex) - 1, encoding);
}
else
{
parameterName = null;
parameterValue = HttpUtility.UrlDecode(bytes, startIndex, i - startIndex, encoding);
}

base.Add(parameterName, parameterValue);

if ((i == (bLen - 1)) && (bytes[i] == 0x26))
{
base.Add(null, string.Empty);
}
}
}

/// <summary>
/// 从多部件的实体主体内容中读取变量填充到集合,文件排除在外。
/// </summary>
/// <param name="bytes"></param>
/// <param name="isUrlDecode"></param>
/// <param name="encoding"></param>
private void FillFromMultipartBytes(byte[] bytes, Encoding encoding)
{
// 字节数组长度
int bLen = (bytes != null) ? bytes.Length : 0;

// 当前字节索引
int currentIndex = 0;

// 当前行开始索引
int lineStartIndex = -1;

// 当前行结束索引
int lineEndIndex = currentIndex;

// 当前行字节长度
int lineLength = -1;

// 是否最后一个分界符
bool isLastBoundary = false;

// 上一行是否分界符行
bool prevIsBoundary = false;

// 上一行是否参数名称行
bool prevIsParaName = false;

// 上一行是否参数名称行的结束索引
int prevParaNameLineEndIndex = 0;

// 参数名称
string paraName = string.Empty;

// 参数值
string paraValue = string.Empty;

// 遍历字节数组
for (int i = 0; i < bLen; i++)
{
//查找行,行由char(13)+char(10)结束
while (lineEndIndex < bLen)
{
// 换行
if (bytes[lineEndIndex] == 10)
{
lineStartIndex = currentIndex;
lineLength = lineEndIndex - currentIndex;

// 回车
if (lineLength > 0 && bytes[lineEndIndex - 1] == 13)
{
lineLength--;
}

currentIndex = lineEndIndex + 1;

break;
}

if (++lineEndIndex == bLen)
{
lineStartIndex = currentIndex;
lineLength = lineEndIndex - currentIndex;
currentIndex = bLen;
}
}

// 处理行
if (lineStartIndex >= 0)
{
// 如果是分界符行
if (AtBoundaryLine(bytes, lineLength, lineStartIndex, out isLastBoundary))
{
// 获取参数值
if (prevIsParaName)
{
paraValue = GetParaValueFromContent(bytes, bLen, prevParaNameLineEndIndex, lineStartIndex, encoding);
prevIsParaName = false;
base.Add(paraName, paraValue);
}

prevIsBoundary = true;

// 最后一行了
if (isLastBoundary)
{
break;
}
}
else
{
// 如果上一行是分界符行,则处理本行
if (prevIsBoundary)
{
if (lineLength <= 0)
{
continue;
}

byte[] buffer = new byte[lineLength];
Array.Copy(bytes, lineStartIndex, buffer, 0, lineLength);

string l = encoding.GetString(buffer);
int colonIndex = l.IndexOf(':');
if (colonIndex >= 0)
{
string str2 = l.Substring(0, colonIndex);

if (string.Equals(str2, "Content-Disposition", StringComparison.CurrentCultureIgnoreCase))
{
// 获取参数名称
paraName = this.GetParaNameFromContent(l, colonIndex + 1, "name");

//// 获取文件名称
//string paraFileName = this.GetParaNameFromContent(l, colonIndex + 1, "filename");

// 参数名不为空,且非文件
if (!string.IsNullOrEmpty(paraName) && !l.Contains("filename"))
{
// 标记上一行是参数名称行
prevIsParaName = true;

// 行结束的索引
prevParaNameLineEndIndex = lineEndIndex;
}
}
}
}

prevIsBoundary = false;
}
}

// 处理下一行
lineEndIndex++;
i = lineEndIndex;
}
}

/// <summary>
/// 获取HTTP实体主体的内容的字节数组形式
/// </summary>
/// <param name="stream"></param>
/// <param name="bufferLen"></param>
/// <returns></returns>
private byte[] GetEntityBody(System.IO.Stream stream, int bufferLen)
{
// 如果指定的无效长度的缓冲区,则指定一个默认的长度作为缓存大小
if (bufferLen < 1)
{
bufferLen = 0x8000;
}

// 初始化一个缓存区
byte[] buffer = new byte[bufferLen];

// 已读取的字节数
int read = 0;

// 缓冲区中的总字节数
int block;

// 每次从流中读取缓存大小的数据,直到读取完所有的流为止
// 如果当前可用的字节数没有请求的字节数那么多,则总字节数可能小于请求的字节数;如果已到达流的末尾,则为零 (0)
while ((block = stream.Read(buffer, read, buffer.Length - read)) > 0)
{
// 重新设定读取位置
read += block;

// 检查已读取字节数是否到达了缓冲区的边界
if (read == buffer.Length)
{
// 尝试读取一个字节,检查是否还有可以读取的信息
int nextByte = stream.ReadByte();

// 读取失败则说明读取完成可以返回结果
if (nextByte == -1)
{
return buffer;
}

// 调整数组大小准备继续读取
byte[] newBuf = new byte[buffer.Length * 2];
Array.Copy(buffer, newBuf, buffer.Length);
newBuf[read] = (byte)nextByte;

// buffer是一个引用(指针),这里意在重新设定buffer指针指向一个更大的内存
buffer = newBuf;
read++;
}
}

// 如果缓存太大则收缩前面while读取的buffer,然后直接返回
byte[] ret = new byte[read];
Array.Copy(buffer, ret, read);
return ret;
}

/// <summary>
/// 获取边界字符串
/// </summary>
/// <returns></returns>
private bool GetMultipartBoundary()
{
// 获取边界字符串属性的值
string attributeFromHeader = GetAttributeFromHeader(contentType, "boundary");
if (attributeFromHeader == null)
{
return false;
}

// 每一个边界符前面都需要加2个连字符“--”
attributeFromHeader = "--" + attributeFromHeader;
boundary = Encoding.ASCII.GetBytes(attributeFromHeader.ToCharArray());

return true;
}

/// <summary>
/// 判断是否在分界符行
/// </summary>
/// <param name="bytes"></param>
/// <param name="lineLength"></param>
/// <param name="lineStartIndex"></param>
/// <param name="isLastBoundary"></param>
/// <returns></returns>
private bool AtBoundaryLine(byte[] bytes, int lineLength, int lineStartIndex, out bool isLastBoundary)
{
isLastBoundary = false;

int length = this.boundary.Length;
if (lineLength != length && lineLength != (length + 2))
{
return false;
}

for (int i = 0; i < length; i++)
{
if (bytes[lineStartIndex + i] != this.boundary[i])
{
return false;
}
}

// 最后一个分界符后两个字符是“--”
if (lineLength != length)
{
if ((bytes[lineStartIndex + length] != 0x2d) || (bytes[(lineStartIndex + length) + 1] != 0x2d))
{
return false;
}

isLastBoundary = true;
}

return true;
}

/// <summary>
/// 获取ContentType中属性的值
/// </summary>
/// <param name="headerValue">ContentType</param>
/// <param name="attrName">属性名称</param>
/// <returns></returns>
private string GetAttributeFromHeader(string headerValue, string attrName)
{
int index;
if (headerValue == null)
{
return null;
}

int headerValueLen = headerValue.Length;
int attrNameLen = attrName.Length;

// 获取attrName的起始索引位置
int startIndex = 1;
while (startIndex < headerValueLen)
{
// ContentType结构类似:multipart/form-data; boundary=---------------------------7db2693010b2a
startIndex = CultureInfo.InvariantCulture.CompareInfo.IndexOf(headerValue, attrName, startIndex, CompareOptions.IgnoreCase);

// 不包含“attrName”,跳出循环
if ((startIndex < 0) || (startIndex + attrNameLen >= headerValueLen))
{
break;
}

// 符合如下条件即跳出循环
// attrName前一个字符可以为 ; 或 , 或 空白(char 11 12 13)
// attrName后一个字符可以为 = 或 空白(char 11 12 13)
char c = headerValue[startIndex - 1];
char ch2 = headerValue[startIndex + attrNameLen];
if ((c == ';' || c == ',' || char.IsWhiteSpace(c)) && ((ch2 == '=') || char.IsWhiteSpace(ch2)))
{
break;
}

// 不符合条件,索引前进,继续查找
startIndex += attrNameLen;
}

// 不包含符合条件的“attrName”
if ((startIndex < 0) || (startIndex >= headerValueLen))
{
return null;
}

// ContentType中包含了attrName,获取attrName的值
startIndex += attrNameLen;

// 如果startIndex是空白,则索引++,直到非空白
while ((startIndex < headerValueLen) && char.IsWhiteSpace(headerValue[startIndex]))
{
startIndex++;
}

// 移动到符号 =
if ((startIndex >= headerValueLen) || (headerValue[startIndex] != '='))
{
return null;
}

// 继续前进到值
startIndex++;

while ((startIndex < headerValueLen) && char.IsWhiteSpace(headerValue[startIndex]))
{
startIndex++;
}

// 如果索引超出,则返回
if (startIndex >= headerValueLen)
{
return null;
}

// 如果是被双引号包含的
if ((startIndex < headerValueLen) && (headerValue[startIndex] == '"'))
{
if (startIndex == (headerValueLen - 1))
{
return null;
}

// 获取结束的双引号
index = headerValue.IndexOf('"', startIndex + 1);

if ((index < 0) || (index == (startIndex + 1)))
{
return null;
}

// 截取双引号之间的值
return headerValue.Substring(startIndex + 1, (index - startIndex) - 1).Trim();
}

// 索引前进,查找空格或逗号等分隔符
// 如果找不到,索引到倒数第二个字符
index = startIndex;
while (index < headerValueLen)
{
if ((headerValue[index] == ' ') || (headerValue[index] == ','))
{
break;
}

index++;
}

if (index == startIndex)
{
return null;
}

// 截取返回
return headerValue.Substring(startIndex, index - startIndex).Trim();
}

/// <summary>
/// 获取参数名称
/// </summary>
/// <param name="l"></param>
/// <param name="pos"></param>
/// <param name="name"></param>
/// <returns></returns>
private string GetParaNameFromContent(string l, int pos, string name)
{
string str = name + "=\"";
int startIndex = CultureInfo.InvariantCulture.CompareInfo.IndexOf(l, str, pos, CompareOptions.IgnoreCase);
if (startIndex < 0)
{
return null;
}
startIndex += str.Length;
int index = l.IndexOf('"', startIndex);
if (index < 0)
{
return null;
}
if (index == startIndex)
{
return string.Empty;
}

return l.Substring(startIndex, index - startIndex);
}

/// <summary>
/// 获取参数值
/// </summary>
/// <param name="bytes"></param>
/// <param name="bLen"></param>
/// <param name="pos"></param>
/// <param name="lineStartIndex"></param>
/// <param name="encoding"></param>
/// <returns></returns>
private string GetParaValueFromContent(byte[] bytes, int bLen, int pos, int lineStartIndex, Encoding encoding)
{
int valueStart = pos + 3;
int valueLength = -1;
int valueEndIndex = lineStartIndex - 1;

if (valueStart > bLen || valueEndIndex > bLen)
{
return null;
}

if (bytes[valueEndIndex] == 10)
{
valueEndIndex--;
}
if (bytes[valueEndIndex] == 13)
{
valueEndIndex--;
}

valueLength = (valueEndIndex - valueStart) + 1;

return encoding.GetString(bytes, valueStart, valueLength);
}
}
}

//注意我在这个类中剔除了获取文件项的方法,只能获取其它表单项的值。
//使用的时候可以如下调用:

//查看源代码
//打印?

/// <summary>
/// 获取以Post方式提交的参数集合。
/// </summary>
/// <param name="isUrlDecode">是否要进行Url解码</param>
/// <param name="encoding">Url解码时用的编码</param>
/// <returns>参数集合。</returns>
/// <example>
/// string paras = string.Empty;
/// System.Collections.Specialized.NameValueCollection paraCollection = RequestHelper.GetFormStrings(Encoding.UTF8);
///
/// foreach (string key in paraCollection.AllKeys)
/// {
/// paras += key + ":" + paraCollection[key] + "\r\n";
/// }
/// </example>
//public static NameValueCollection GetFormStrings(Encoding encoding)
//{
//return new PostVariableCollection(encoding);
//}

在Global.asax中过滤POST请求的非法参数。的更多相关文章

  1. 如何在Global.asax中判断是否是ajax请求

    今天在一个应用场景中需要在Global.asax中判断一个请求是否是ajax请求,而在ASP.NET MVC中已经提供了一个现成的扩展方法IsAjaxRequest: namespace System ...

  2. 定时发布任务,在global.asax中获取文件的物理路径的方法

    如果要把一个相对路径或者虚拟路径映射道服务器的物理路径,通常会使用Server.MapPath()函数,比如将根目录下的html目录映射为物理路径:Server.MapPath("html& ...

  3. Global.asax 中校验Session

    Application 相关的 Application_Init:在每一个HttpApplication实例初始化的时候执行. Application_Disposed:在每一个HttpApplica ...

  4. 在Asp.Net的Global.asax中Application_Error跳转到自定义错误页无效的解决办法

    在开发Asp.Net系统的时候,我们很多时候希望系统发生错误后能够跳转到一个自定义的错误页面,于是我们经常会在Global.asax中的Application_Error方法中使用Response.R ...

  5. Global.asax中的操作数据库代码无法执行

    本人最近在做一个基于Access数据库的Web应用程序,为了实现一个定时更新数据库的需求,我在Global.asax中的Application_Start函数里写了个计时器, void Applica ...

  6. 在ASP.Net MVC 中,如何在Global.asax中配置一个指向Area内部的默认Route

    ASP.Net MVC 中配置Route的时候可以设置一个默认的Route. 比如我要在输入http://localhost的时候默认进入http://localhost/home/index.可以在 ...

  7. 关于在 ASP.NET 的 Global.asax 中 Application_Error 方法内,设置跳转到自定义错误页无效的问题

    转自:https://www.cnblogs.com/OpenCoder/p/5070645.html 在 Global.asax 中的 Application_Error 方法中,使用 Respon ...

  8. .net全局定时定期执行某些操作在Global.asax中具体实现

    全局定时定期执行某些操作看起来是多么自动化的一个问题不过在.net的Global.asax文件中稍微配置即可实现,详细配置如下,感兴趣的朋友可以参考下哈 <%@ Application Lang ...

  9. jquery中的ajax请求用法以及参数详情

    url: 要求为String类型的参数,(默认为当前页地址)发送请求的地址. type: 要求为String类型的参数,请求方式(post或get)默认为get.注意其他http请求方法,例如put和 ...

随机推荐

  1. 001---C/S架构

    C/S 架构介绍 什么是C/S架构 C:client,客户端 S:server,服务端 实现客户端和服务端之间的网络通信 什么是网络 人与人之间交流是通过语言,才能彼此理解对方的意思.但是地球上有多个 ...

  2. python2.7入门---file(文件)&OS 文件&目录方法

        首先file 对象使用 open 函数来创建,下表列出了 file 对象常用的函数: 序号 方法及描述 1 file.close() 关闭文件.关闭后文件不能再进行读写操作. 2 file.f ...

  3. python2.7练习小例子(二十五)

        25):题目:有5个人坐在一起,问第五个人多少岁?他说比第4个人大2岁.问第4个人岁数,他说比第3个人大2岁.问第三个人,又说比第2人大两岁.问第2个人,说比第一个人大两岁.最后问第一个人,他 ...

  4. PHP HashTable总结

    本篇文章主要是对 PHP HashTable 总结,下面的参考链接是很好的学习资料. 总结 HashTable 又叫做散列表,是一种用于以常数平均时间执行插入.删除和查找的技术.不能有效的支持元素之间 ...

  5. 仿造vue-resource的formdata传对象

    众插件不支持同步,也是没办法的事情,具体为啥就不分析了,确实搞不懂. 一直用vue-resource的post,觉得很舒服. 然,没办法只能仿造一个,自己提供一个同步方法 几个点先摆清楚 1. .th ...

  6. EAS集锦

    前言 之前看过的相关BOS开发文档,整理了一些常用的API,一直没有来得及放上来,现在把整理的文件放上来,以备忘查看,分享.闲话少说,上干货! ps 图片不方便查看的话,可以拖住图片,加载到浏览器新页 ...

  7. Spring研磨分析、Quartz任务调度、Hibernate深入浅出系列文章笔记汇总

    Spring研磨分析.Quartz任务调度.Hibernate深入浅出系列文章笔记汇总 置顶2017年04月27日 10:46:45 阅读数:1213 这系列文章主要是对Spring.Quartz.H ...

  8. vuex模块相互调用

    https://segmentfault.com/a/1190000009434398

  9. Vue学习(一):Vue实例

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  10. 1.16. BIP39协议:使用助记词生成确定性钱包

    以太坊系统学习教程: https://www.netkiller.cn/blockchain/bip39.html 1.16. BIP39协议:使用助记词生成确定性钱包 BIP:39 层:应用层 标题 ...