asp.net自定义获取Get和Post请求的参数

来源:互联网 发布:opencv java 高斯模糊 编辑:程序博客网 时间:2024/05/16 07:29

转自:http://blog.csdn.net/jpr1990/article/details/7240513

关于获取Get和Post请求的参数,.net类库提供了相关的方法:

Request.QueryString  常见的获取Url参数。

Request.Form 常见的获取提交表单项。

这两个方法在获取的时候都会进行解码,并且不是使用者可以控制的。这样可能就会导致一些问题,比如不想对获取到的参数进行解码,或者解码的时候需要特殊的编码而不是系统默认的编码。当然也可以从请求的数据流中读取字节进行解析,这样就增加了处理的复杂度,并且不便于快速多处应用。

这篇文章将提供一些方法,在获取参数的时候指定是否解码以及编码的类型。

一、首先看Url方式提交的参数

复制代码
  1 /// <summary>  2 /// 获取以Url方式提交的参数集合。  3 /// </summary>  4 /// <param name="isUrlDecode">是否要进行Url解码</param>  5 /// <param name="encoding">Url解码时用的编码</param>  6 /// <returns>参数集合。</returns>  7 /// <example>  8 /// string paras = string.Empty;  9 /// System.Collections.Specialized.NameValueCollection paraCollection = RequestHelper.GetQueryStrings(true, Encoding.UTF8); 10 /// 11 /// foreach (string key in paraCollection.AllKeys) 12 /// { 13 ///     paras += key + ":" + paraCollection[key] + "\r\n"; 14 /// } 15 /// </example> 16 public static NameValueCollection GetQueryStrings(bool isUrlDecode, Encoding encoding) 17 { 18 // 获取查询字符串 19 string query = HttpContext.Current.Request.Url.Query; 20 if (query.StartsWith("?")) 21 { 22 if (query.Length > 1) 23 { 24 query = query.Substring(1, query.Length - 1); 25 } 26 else 27 { 28 query = string.Empty; 29 } 30 } 31    32 // 处理查询字符串 33 NameValueCollection collection = FillFromString(query, isUrlDecode, encoding); 34 return collection; 35 } 36    37 /// <summary> 38 /// 从参数字符串获取参数集合 39 /// </summary> 40 /// <param name="s">参数字符串</param> 41 /// <param name="isUrlDecode">是否要进行Url解码</param> 42 /// <param name="encoding">Url解码时用的编码</param> 43 /// <returns>Url参数集合</returns> 44 private static NameValueCollection FillFromString(string s, bool isUrlDecode, Encoding encoding) 45 { 46 NameValueCollection parametersCollection = new NameValueCollection(); 47    48 // 参数字符串长度 49 int sLen = (s != null) ? s.Length : 0; 50    51 // 遍历每个字符 52 for (int i = 0; i < sLen; i++) 53 { 54 // 参数名开始位置 55 int startIndex = i; 56    57 // 参数名结束位置 58 int endIndex = -1; 59    60 // 字符索引前进,直到遇到等号,更新结束的索引位置 61 // 如果遇到&符号,则参数结束,退出循环 62 while (i < sLen) 63 { 64 char ch = s[i]; 65 if (ch == '=') 66 { 67 if (endIndex < 0) 68 { 69 endIndex = i; 70 } 71 } 72 else if (ch == '&') 73 { 74 break; 75 } 76    77 // 字符索引前进 78 i++; 79 } 80    81 string parameterName = null; 82 string parameterValue = null; 83    84 // 存在等号,可以获取到参数名和参数值 85 if (endIndex >= 0) 86 { 87 parameterName = s.Substring(startIndex, endIndex - startIndex); 88 parameterValue = s.Substring(endIndex + 1, (i - endIndex) - 1); 89 } 90 else 91 { 92 parameterValue = s.Substring(startIndex, i - startIndex); 93 } 94    95 // 需要解码 96 if (isUrlDecode) 97 { 98 parametersCollection.Add(HttpUtility.UrlDecode(parameterName, encoding), HttpUtility.UrlDecode(parameterValue, encoding)); 99 }100 else101 {102 parametersCollection.Add(parameterName, parameterValue);103 }104   105 // 最后一个字符是 &,则添加一个参数为null的NameValue对。106 if ((i == (sLen - 1)) && (s[i] == '&'))107 {108 parametersCollection.Add(null, string.Empty);109 }110 }111   112 return parametersCollection;113 }114  
复制代码

FillFromString方法是通过反编译微软的类库,然后经过简单修改而来的。处理方式严谨可靠,学习了下。

使用的时候调用GetQueryStrings方法获取全部Get参数的集合。

二、获取Post方式提交的参数

相比获取通过Url方式提交的参数,获取通过Post方式提交的参数要复杂一些。

要区分两种表单的类型:application/x-www-form-urlencoded 和 multipart/form-data,前者只能提交一般参数,后者还可以提交文件。

因为通过这种方式提交的数据,是先从流中读取字节数据,然后解码的,所以解码是必须的,但是可以提供特殊的编码类型。

我这里专门定义了一个类来解析这些数据,当然这个方法也是按照微软的思路来做的。

复制代码
  1 using System;  2 using System.Collections.Generic;  3 using System.Collections.Specialized;  4 using System.Linq;  5 using System.Text;  6 using System.Web;  7 using System.Globalization;  8     9 namespace VeryCodes.Web 10 { 11 /// <summary> 12 /// 以Post方式提交的变量的集合。 13 /// </summary> 14 /// <remarks> 15 /// 不包含提交的文件。 16 /// </remarks> 17 internal class PostVariableCollection : NameValueCollection 18 { 19 /// <summary> 20 /// Content Type 21 /// </summary> 22 private string contentType = string.Empty; 23    24 /// <summary> 25 /// 分界符 26 /// </summary> 27 private byte[] boundary; 28    29 /// <summary> 30 /// 初始化类 PostVariableCollection 的一个新实例 31 /// </summary> 32 public PostVariableCollection() 33 { 34 FillFormStream(Encoding.Default); 35 } 36    37 /// <summary> 38 /// 初始化类 PostVariableCollection 的一个新实例 39 /// </summary> 40 /// <param name="isUrlDecode">是否进行Url解码</param> 41 /// <param name="encoding">编码类型</param> 42 public PostVariableCollection(Encoding encoding) 43 { 44 FillFormStream(encoding); 45 } 46    47 /// <summary> 48 /// 使用HTTP实体主体内容填充集合 49 /// </summary> 50 /// <param name="isUrlDecode"></param> 51 /// <param name="encoding"></param> 52 private void FillFormStream(Encoding encoding) 53 { 54 contentType = HttpContext.Current.Request.ContentType; 55    56 if (!string.IsNullOrEmpty(contentType)) 57 { 58 System.IO.Stream entityStream = HttpContext.Current.Request.InputStream; 59    60 // 获取HTTP实体主体的内容 61 byte[] bytes = GetEntityBody(entityStream, 0); 62    63 if (bytes == null || bytes.Length <= 0) 64 { 65 return; 66 } 67    68 // 因为是字节数据,所有的数据都需要解码 69 if (contentType.StartsWith("application/x-www-form-urlencoded", System.StringComparison.CurrentCultureIgnoreCase)) 70 { 71 try 72 { 73 FillFromBytes(bytes, encoding); 74 return; 75 } 76 catch (Exception ex) 77 { 78 throw new HttpException("Invalid_urlencoded_form_data", ex); 79 } 80 } 81    82 if (contentType.StartsWith("multipart/form-data", System.StringComparison.CurrentCultureIgnoreCase)) 83 { 84 if (GetMultipartBoundary()) 85 { 86 try 87 { 88 // 获取各个参数 89 FillFromMultipartBytes(bytes, encoding); 90 return; 91 } 92 catch (Exception ex) 93 { 94 throw new HttpException("Invalid_multipart_form_data", ex); 95 } 96 } 97 } 98 } 99 }100   101 /// <summary>102 /// 从字节数组读取变量填充到集合103 /// </summary>104 /// <param name="bytes"></param>105 /// <param name="encoding"></param>106 private void FillFromBytes(byte[] bytes, Encoding encoding)107 {108 // 字节数组长度109 int bLen = (bytes != null) ? bytes.Length : 0;110   111 // 遍历字节数组112 for (int i = 0; i < bLen; i++)113 {114 string parameterName;115 string parameterValue;116   117 //参数名开始位置118 int startIndex = i;119   120 //参数名结束位置121 int endIndex = -1;122   123 while (i < bLen)124 {125 byte bt = bytes[i];126   127 // 符号:=128 if (bt == 0x3d)129 {130 if (endIndex < 0)131 {132 endIndex = i;133 }134 }135 else if (bt == 0x26) //符号:&136 {137 break;138 }139 i++;140 }141   142 if (endIndex >= 0)143 {144 parameterName = HttpUtility.UrlDecode(bytes, startIndex, endIndex - startIndex, encoding);145 parameterValue = HttpUtility.UrlDecode(bytes, endIndex + 1, (i - endIndex) - 1, encoding);146 }147 else148 {149 parameterName = null;150 parameterValue = HttpUtility.UrlDecode(bytes, startIndex, i - startIndex, encoding);151 }152   153 base.Add(parameterName, parameterValue);154   155 if ((i == (bLen - 1)) && (bytes[i] == 0x26))156 {157 base.Add(null, string.Empty);158 }159 }160 }161   162 /// <summary>163 /// 从多部件的实体主体内容中读取变量填充到集合,文件排除在外。164 /// </summary>165 /// <param name="bytes"></param>166 /// <param name="isUrlDecode"></param>167 /// <param name="encoding"></param>168 private void FillFromMultipartBytes(byte[] bytes, Encoding encoding)169 {170 // 字节数组长度171 int bLen = (bytes != null) ? bytes.Length : 0;172   173 // 当前字节索引174 int currentIndex = 0;175   176 // 当前行开始索引177 int lineStartIndex = -1;178   179 // 当前行结束索引180 int lineEndIndex = currentIndex;181   182 // 当前行字节长度183 int lineLength = -1;184   185 // 是否最后一个分界符186 bool isLastBoundary = false;187   188 // 上一行是否分界符行189 bool prevIsBoundary = false;190   191 // 上一行是否参数名称行192 bool prevIsParaName = false;193   194 // 上一行是否参数名称行的结束索引195 int prevParaNameLineEndIndex = 0;196   197 // 参数名称198 string paraName = string.Empty;199   200 // 参数值201 string paraValue = string.Empty;202   203 // 遍历字节数组204 for (int i = 0; i < bLen; i++)205 {206 //查找行,行由char(13)+char(10)结束207 while (lineEndIndex < bLen)208 {209 // 换行210 if (bytes[lineEndIndex] == 10)211 {212 lineStartIndex = currentIndex;213 lineLength = lineEndIndex - currentIndex;214   215 // 回车216 if (lineLength > 0 && bytes[lineEndIndex - 1] == 13)217 {218 lineLength--;219 }220   221 currentIndex = lineEndIndex + 1;222   223 break;224 }225   226 if (++lineEndIndex == bLen)227 {228 lineStartIndex = currentIndex;229 lineLength = lineEndIndex - currentIndex;230 currentIndex = bLen;231 }232 }233   234 // 处理行235 if (lineStartIndex >= 0)236 {237 // 如果是分界符行238 if (AtBoundaryLine(bytes, lineLength, lineStartIndex, out isLastBoundary))239 {240 // 获取参数值241 if (prevIsParaName)242 {243 paraValue = GetParaValueFromContent(bytes, bLen, prevParaNameLineEndIndex, lineStartIndex, encoding);244 prevIsParaName = false;245 base.Add(paraName, paraValue);246 }247   248 prevIsBoundary = true;249   250 // 最后一行了251 if (isLastBoundary)252 {253 break;254 }255 }256 else257 {258 // 如果上一行是分界符行,则处理本行259 if (prevIsBoundary)260 {261 if (lineLength <= 0)262 {263 continue;264 }265   266 byte[] buffer = new byte[lineLength];267 Array.Copy(bytes, lineStartIndex, buffer, 0, lineLength);268   269 string l = encoding.GetString(buffer);270 int colonIndex = l.IndexOf(':');271 if (colonIndex >= 0)272 {273 string str2 = l.Substring(0, colonIndex);274   275 if (string.Equals(str2, "Content-Disposition", StringComparison.CurrentCultureIgnoreCase))276 {277 // 获取参数名称278 paraName = this.GetParaNameFromContent(l, colonIndex + 1, "name");279   280 //// 获取文件名称281 //string paraFileName = this.GetParaNameFromContent(l, colonIndex + 1, "filename");282   283 // 参数名不为空,且非文件284 if (!string.IsNullOrEmpty(paraName) && !l.Contains("filename"))285 {286 // 标记上一行是参数名称行287 prevIsParaName = true;288   289 // 行结束的索引290 prevParaNameLineEndIndex = lineEndIndex;291 }292 }293 }294 }295   296 prevIsBoundary = false;297 }298 }299   300 // 处理下一行301 lineEndIndex++;302 i = lineEndIndex;303 }304 }305   306 /// <summary>307 /// 获取HTTP实体主体的内容的字节数组形式308 /// </summary>309 /// <param name="stream"></param>310 /// <param name="bufferLen"></param>311 /// <returns></returns>312 private byte[] GetEntityBody(System.IO.Stream stream, int bufferLen)313 {314 // 如果指定的无效长度的缓冲区,则指定一个默认的长度作为缓存大小315 if (bufferLen < 1)316 {317 bufferLen = 0x8000;318 }319   320 // 初始化一个缓存区321 byte[] buffer = new byte[bufferLen];322   323 // 已读取的字节数324 int read = 0;325   326 // 缓冲区中的总字节数327 int block;328   329 // 每次从流中读取缓存大小的数据,直到读取完所有的流为止330 // 如果当前可用的字节数没有请求的字节数那么多,则总字节数可能小于请求的字节数;如果已到达流的末尾,则为零 (0)331 while ((block = stream.Read(buffer, read, buffer.Length - read)) > 0)332 {333 // 重新设定读取位置334 read += block;335   336 // 检查已读取字节数是否到达了缓冲区的边界337 if (read == buffer.Length)338 {339 // 尝试读取一个字节,检查是否还有可以读取的信息340 int nextByte = stream.ReadByte();341   342 // 读取失败则说明读取完成可以返回结果343 if (nextByte == -1)344 {345 return buffer;346 }347   348 // 调整数组大小准备继续读取349 byte[] newBuf = new byte[buffer.Length * 2];350 Array.Copy(buffer, newBuf, buffer.Length);351 newBuf[read] = (byte)nextByte;352   353 // buffer是一个引用(指针),这里意在重新设定buffer指针指向一个更大的内存354 buffer = newBuf;355 read++;356 }357 }358   359 // 如果缓存太大则收缩前面while读取的buffer,然后直接返回360 byte[] ret = new byte[read];361 Array.Copy(buffer, ret, read);362 return ret;363 }364   365 /// <summary>366 /// 获取边界字符串367 /// </summary>368 /// <returns></returns>369 private bool GetMultipartBoundary()370 {371 // 获取边界字符串属性的值372 string attributeFromHeader = GetAttributeFromHeader(contentType, "boundary");373 if (attributeFromHeader == null)374 {375 return false;376 }377   378 // 每一个边界符前面都需要加2个连字符“--”379 attributeFromHeader = "--" + attributeFromHeader;380 boundary = Encoding.ASCII.GetBytes(attributeFromHeader.ToCharArray());381   382 return true;383 }384   385 /// <summary>386 /// 判断是否在分界符行387 /// </summary>388 /// <param name="bytes"></param>389 /// <param name="lineLength"></param>390 /// <param name="lineStartIndex"></param>391 /// <param name="isLastBoundary"></param>392 /// <returns></returns>393 private bool AtBoundaryLine(byte[] bytes, int lineLength, int lineStartIndex, out bool isLastBoundary)394 {395 isLastBoundary = false;396   397 int length = this.boundary.Length;398 if (lineLength != length && lineLength != (length + 2))399 {400 return false;401 }402   403 for (int i = 0; i < length; i++)404 {405 if (bytes[lineStartIndex + i] != this.boundary[i])406 {407 return false;408 }409 }410   411 // 最后一个分界符后两个字符是“--”412 if (lineLength != length)413 {414 if ((bytes[lineStartIndex + length] != 0x2d) || (bytes[(lineStartIndex + length) + 1] != 0x2d))415 {416 return false;417 }418   419 isLastBoundary = true;420 }421   422 return true;423 }424   425 /// <summary>426 /// 获取ContentType中属性的值427 /// </summary>428 /// <param name="headerValue">ContentType</param>429 /// <param name="attrName">属性名称</param>430 /// <returns></returns>431 private string GetAttributeFromHeader(string headerValue, string attrName)432 {433 int index;434 if (headerValue == null)435 {436 return null;437 }438   439 int headerValueLen = headerValue.Length;440 int attrNameLen = attrName.Length;441   442 // 获取attrName的起始索引位置443 int startIndex = 1;444 while (startIndex < headerValueLen)445 {446 // ContentType结构类似:multipart/form-data; boundary=---------------------------7db2693010b2a447 startIndex = CultureInfo.InvariantCulture.CompareInfo.IndexOf(headerValue, attrName, startIndex, CompareOptions.IgnoreCase);448   449 // 不包含“attrName”,跳出循环450 if ((startIndex < 0) || (startIndex + attrNameLen >= headerValueLen))451 {452 break;453 }454   455 // 符合如下条件即跳出循环456 // attrName前一个字符可以为 ; 或 , 或 空白(char 11 12 13)457 // attrName后一个字符可以为 = 或 空白(char 11 12 13)458 char c = headerValue[startIndex - 1];459 char ch2 = headerValue[startIndex + attrNameLen];460 if ((c == ';' || c == ',' || char.IsWhiteSpace(c)) && ((ch2 == '=') || char.IsWhiteSpace(ch2)))461 {462 break;463 }464   465 // 不符合条件,索引前进,继续查找466 startIndex += attrNameLen;467 }468   469 // 不包含符合条件的“attrName”470 if ((startIndex < 0) || (startIndex >= headerValueLen))471 {472 return null;473 }474   475 // ContentType中包含了attrName,获取attrName的值476 startIndex += attrNameLen;477   478 // 如果startIndex是空白,则索引++,直到非空白479 while ((startIndex < headerValueLen) && char.IsWhiteSpace(headerValue[startIndex]))480 {481 startIndex++;482 }483   484 // 移动到符号 =485 if ((startIndex >= headerValueLen) || (headerValue[startIndex] != '='))486 {487 return null;488 }489   490 // 继续前进到值491 startIndex++;492   493 while ((startIndex < headerValueLen) && char.IsWhiteSpace(headerValue[startIndex]))494 {495 startIndex++;496 }497   498 // 如果索引超出,则返回499 if (startIndex >= headerValueLen)500 {501 return null;502 }503   504 // 如果是被双引号包含的505 if ((startIndex < headerValueLen) && (headerValue[startIndex] == '"'))506 {507 if (startIndex == (headerValueLen - 1))508 {509 return null;510 }511   512 // 获取结束的双引号513 index = headerValue.IndexOf('"', startIndex + 1);514   515 if ((index < 0) || (index == (startIndex + 1)))516 {517 return null;518 }519   520 // 截取双引号之间的值521 return headerValue.Substring(startIndex + 1, (index - startIndex) - 1).Trim();522 }523   524 // 索引前进,查找空格或逗号等分隔符525 // 如果找不到,索引到倒数第二个字符526 index = startIndex;527 while (index < headerValueLen)528 {529 if ((headerValue[index] == ' ') || (headerValue[index] == ','))530 {531 break;532 }533   534 index++;535 }536   537 if (index == startIndex)538 {539 return null;540 }541   542 // 截取返回543 return headerValue.Substring(startIndex, index - startIndex).Trim();544 }545   546 /// <summary>547 /// 获取参数名称548 /// </summary>549 /// <param name="l"></param>550 /// <param name="pos"></param>551 /// <param name="name"></param>552 /// <returns></returns>553 private string GetParaNameFromContent(string l, int pos, string name)554 {555 string str = name + "=\"";556 int startIndex = CultureInfo.InvariantCulture.CompareInfo.IndexOf(l, str, pos, CompareOptions.IgnoreCase);557 if (startIndex < 0)558 {559 return null;560 }561 startIndex += str.Length;562 int index = l.IndexOf('"', startIndex);563 if (index < 0)564 {565 return null;566 }567 if (index == startIndex)568 {569 return string.Empty;570 }571   572 return l.Substring(startIndex, index - startIndex);573 }574   575 /// <summary>576 /// 获取参数值577 /// </summary>578 /// <param name="bytes"></param>579 /// <param name="bLen"></param>580 /// <param name="pos"></param>581 /// <param name="lineStartIndex"></param>582 /// <param name="encoding"></param>583 /// <returns></returns>584 private string GetParaValueFromContent(byte[] bytes, int bLen, int pos, int lineStartIndex, Encoding encoding)585 {586 int valueStart = pos + 3;587 int valueLength = -1;588 int valueEndIndex = lineStartIndex - 1;589   590 if (valueStart > bLen || valueEndIndex > bLen)591 {592 return null;593 }594   595 if (bytes[valueEndIndex] == 10)596 {597 valueEndIndex--;598 }599 if (bytes[valueEndIndex] == 13)600 {601 valueEndIndex--;602 }603   604 valueLength = (valueEndIndex - valueStart) + 1;605   606 return encoding.GetString(bytes, valueStart, valueLength);607 }608 }609 }610 注意我在这个类中剔除了获取文件项的方法,只能获取其它表单项的值。611 使用的时候可以如下调用:612 613 查看源代码614 打印?615 /// <summary>616 /// 获取以Post方式提交的参数集合。617 /// </summary>618 /// <param name="isUrlDecode">是否要进行Url解码</param>619 /// <param name="encoding">Url解码时用的编码</param>620 /// <returns>参数集合。</returns>621 /// <example>622 /// string paras = string.Empty;623 /// System.Collections.Specialized.NameValueCollection paraCollection = RequestHelper.GetFormStrings(Encoding.UTF8);624 ///625 /// foreach (string key in paraCollection.AllKeys)626 /// {627 ///     paras += key + ":" + paraCollection[key] + "\r\n";628 /// }629 /// </example>630 public static NameValueCollection GetFormStrings(Encoding encoding)631 {632 return new PostVariableCollection(encoding);633 }
复制代码

转自:http://blog.csdn.net/jpr1990/article/details/7240513

关于获取Get和Post请求的参数,.net类库提供了相关的方法:

Request.QueryString  常见的获取Url参数。

Request.Form 常见的获取提交表单项。

这两个方法在获取的时候都会进行解码,并且不是使用者可以控制的。这样可能就会导致一些问题,比如不想对获取到的参数进行解码,或者解码的时候需要特殊的编码而不是系统默认的编码。当然也可以从请求的数据流中读取字节进行解析,这样就增加了处理的复杂度,并且不便于快速多处应用。

这篇文章将提供一些方法,在获取参数的时候指定是否解码以及编码的类型。

一、首先看Url方式提交的参数

复制代码
  1 /// <summary>  2 /// 获取以Url方式提交的参数集合。  3 /// </summary>  4 /// <param name="isUrlDecode">是否要进行Url解码</param>  5 /// <param name="encoding">Url解码时用的编码</param>  6 /// <returns>参数集合。</returns>  7 /// <example>  8 /// string paras = string.Empty;  9 /// System.Collections.Specialized.NameValueCollection paraCollection = RequestHelper.GetQueryStrings(true, Encoding.UTF8); 10 /// 11 /// foreach (string key in paraCollection.AllKeys) 12 /// { 13 ///     paras += key + ":" + paraCollection[key] + "\r\n"; 14 /// } 15 /// </example> 16 public static NameValueCollection GetQueryStrings(bool isUrlDecode, Encoding encoding) 17 { 18 // 获取查询字符串 19 string query = HttpContext.Current.Request.Url.Query; 20 if (query.StartsWith("?")) 21 { 22 if (query.Length > 1) 23 { 24 query = query.Substring(1, query.Length - 1); 25 } 26 else 27 { 28 query = string.Empty; 29 } 30 } 31    32 // 处理查询字符串 33 NameValueCollection collection = FillFromString(query, isUrlDecode, encoding); 34 return collection; 35 } 36    37 /// <summary> 38 /// 从参数字符串获取参数集合 39 /// </summary> 40 /// <param name="s">参数字符串</param> 41 /// <param name="isUrlDecode">是否要进行Url解码</param> 42 /// <param name="encoding">Url解码时用的编码</param> 43 /// <returns>Url参数集合</returns> 44 private static NameValueCollection FillFromString(string s, bool isUrlDecode, Encoding encoding) 45 { 46 NameValueCollection parametersCollection = new NameValueCollection(); 47    48 // 参数字符串长度 49 int sLen = (s != null) ? s.Length : 0; 50    51 // 遍历每个字符 52 for (int i = 0; i < sLen; i++) 53 { 54 // 参数名开始位置 55 int startIndex = i; 56    57 // 参数名结束位置 58 int endIndex = -1; 59    60 // 字符索引前进,直到遇到等号,更新结束的索引位置 61 // 如果遇到&符号,则参数结束,退出循环 62 while (i < sLen) 63 { 64 char ch = s[i]; 65 if (ch == '=') 66 { 67 if (endIndex < 0) 68 { 69 endIndex = i; 70 } 71 } 72 else if (ch == '&') 73 { 74 break; 75 } 76    77 // 字符索引前进 78 i++; 79 } 80    81 string parameterName = null; 82 string parameterValue = null; 83    84 // 存在等号,可以获取到参数名和参数值 85 if (endIndex >= 0) 86 { 87 parameterName = s.Substring(startIndex, endIndex - startIndex); 88 parameterValue = s.Substring(endIndex + 1, (i - endIndex) - 1); 89 } 90 else 91 { 92 parameterValue = s.Substring(startIndex, i - startIndex); 93 } 94    95 // 需要解码 96 if (isUrlDecode) 97 { 98 parametersCollection.Add(HttpUtility.UrlDecode(parameterName, encoding), HttpUtility.UrlDecode(parameterValue, encoding)); 99 }100 else101 {102 parametersCollection.Add(parameterName, parameterValue);103 }104   105 // 最后一个字符是 &,则添加一个参数为null的NameValue对。106 if ((i == (sLen - 1)) && (s[i] == '&'))107 {108 parametersCollection.Add(null, string.Empty);109 }110 }111   112 return parametersCollection;113 }114  
复制代码

FillFromString方法是通过反编译微软的类库,然后经过简单修改而来的。处理方式严谨可靠,学习了下。

使用的时候调用GetQueryStrings方法获取全部Get参数的集合。

二、获取Post方式提交的参数

相比获取通过Url方式提交的参数,获取通过Post方式提交的参数要复杂一些。

要区分两种表单的类型:application/x-www-form-urlencoded 和 multipart/form-data,前者只能提交一般参数,后者还可以提交文件。

因为通过这种方式提交的数据,是先从流中读取字节数据,然后解码的,所以解码是必须的,但是可以提供特殊的编码类型。

我这里专门定义了一个类来解析这些数据,当然这个方法也是按照微软的思路来做的。

复制代码
  1 using System;  2 using System.Collections.Generic;  3 using System.Collections.Specialized;  4 using System.Linq;  5 using System.Text;  6 using System.Web;  7 using System.Globalization;  8     9 namespace VeryCodes.Web 10 { 11 /// <summary> 12 /// 以Post方式提交的变量的集合。 13 /// </summary> 14 /// <remarks> 15 /// 不包含提交的文件。 16 /// </remarks> 17 internal class PostVariableCollection : NameValueCollection 18 { 19 /// <summary> 20 /// Content Type 21 /// </summary> 22 private string contentType = string.Empty; 23    24 /// <summary> 25 /// 分界符 26 /// </summary> 27 private byte[] boundary; 28    29 /// <summary> 30 /// 初始化类 PostVariableCollection 的一个新实例 31 /// </summary> 32 public PostVariableCollection() 33 { 34 FillFormStream(Encoding.Default); 35 } 36    37 /// <summary> 38 /// 初始化类 PostVariableCollection 的一个新实例 39 /// </summary> 40 /// <param name="isUrlDecode">是否进行Url解码</param> 41 /// <param name="encoding">编码类型</param> 42 public PostVariableCollection(Encoding encoding) 43 { 44 FillFormStream(encoding); 45 } 46    47 /// <summary> 48 /// 使用HTTP实体主体内容填充集合 49 /// </summary> 50 /// <param name="isUrlDecode"></param> 51 /// <param name="encoding"></param> 52 private void FillFormStream(Encoding encoding) 53 { 54 contentType = HttpContext.Current.Request.ContentType; 55    56 if (!string.IsNullOrEmpty(contentType)) 57 { 58 System.IO.Stream entityStream = HttpContext.Current.Request.InputStream; 59    60 // 获取HTTP实体主体的内容 61 byte[] bytes = GetEntityBody(entityStream, 0); 62    63 if (bytes == null || bytes.Length <= 0) 64 { 65 return; 66 } 67    68 // 因为是字节数据,所有的数据都需要解码 69 if (contentType.StartsWith("application/x-www-form-urlencoded", System.StringComparison.CurrentCultureIgnoreCase)) 70 { 71 try 72 { 73 FillFromBytes(bytes, encoding); 74 return; 75 } 76 catch (Exception ex) 77 { 78 throw new HttpException("Invalid_urlencoded_form_data", ex); 79 } 80 } 81    82 if (contentType.StartsWith("multipart/form-data", System.StringComparison.CurrentCultureIgnoreCase)) 83 { 84 if (GetMultipartBoundary()) 85 { 86 try 87 { 88 // 获取各个参数 89 FillFromMultipartBytes(bytes, encoding); 90 return; 91 } 92 catch (Exception ex) 93 { 94 throw new HttpException("Invalid_multipart_form_data", ex); 95 } 96 } 97 } 98 } 99 }100   101 /// <summary>102 /// 从字节数组读取变量填充到集合103 /// </summary>104 /// <param name="bytes"></param>105 /// <param name="encoding"></param>106 private void FillFromBytes(byte[] bytes, Encoding encoding)107 {108 // 字节数组长度109 int bLen = (bytes != null) ? bytes.Length : 0;110   111 // 遍历字节数组112 for (int i = 0; i < bLen; i++)113 {114 string parameterName;115 string parameterValue;116   117 //参数名开始位置118 int startIndex = i;119   120 //参数名结束位置121 int endIndex = -1;122   123 while (i < bLen)124 {125 byte bt = bytes[i];126   127 // 符号:=128 if (bt == 0x3d)129 {130 if (endIndex < 0)131 {132 endIndex = i;133 }134 }135 else if (bt == 0x26) //符号:&136 {137 break;138 }139 i++;140 }141   142 if (endIndex >= 0)143 {144 parameterName = HttpUtility.UrlDecode(bytes, startIndex, endIndex - startIndex, encoding);145 parameterValue = HttpUtility.UrlDecode(bytes, endIndex + 1, (i - endIndex) - 1, encoding);146 }147 else148 {149 parameterName = null;150 parameterValue = HttpUtility.UrlDecode(bytes, startIndex, i - startIndex, encoding);151 }152   153 base.Add(parameterName, parameterValue);154   155 if ((i == (bLen - 1)) && (bytes[i] == 0x26))156 {157 base.Add(null, string.Empty);158 }159 }160 }161   162 /// <summary>163 /// 从多部件的实体主体内容中读取变量填充到集合,文件排除在外。164 /// </summary>165 /// <param name="bytes"></param>166 /// <param name="isUrlDecode"></param>167 /// <param name="encoding"></param>168 private void FillFromMultipartBytes(byte[] bytes, Encoding encoding)169 {170 // 字节数组长度171 int bLen = (bytes != null) ? bytes.Length : 0;172   173 // 当前字节索引174 int currentIndex = 0;175   176 // 当前行开始索引177 int lineStartIndex = -1;178   179 // 当前行结束索引180 int lineEndIndex = currentIndex;181   182 // 当前行字节长度183 int lineLength = -1;184   185 // 是否最后一个分界符186 bool isLastBoundary = false;187   188 // 上一行是否分界符行189 bool prevIsBoundary = false;190   191 // 上一行是否参数名称行192 bool prevIsParaName = false;193   194 // 上一行是否参数名称行的结束索引195 int prevParaNameLineEndIndex = 0;196   197 // 参数名称198 string paraName = string.Empty;199   200 // 参数值201 string paraValue = string.Empty;202   203 // 遍历字节数组204 for (int i = 0; i < bLen; i++)205 {206 //查找行,行由char(13)+char(10)结束207 while (lineEndIndex < bLen)208 {209 // 换行210 if (bytes[lineEndIndex] == 10)211 {212 lineStartIndex = currentIndex;213 lineLength = lineEndIndex - currentIndex;214   215 // 回车216 if (lineLength > 0 && bytes[lineEndIndex - 1] == 13)217 {218 lineLength--;219 }220   221 currentIndex = lineEndIndex + 1;222   223 break;224 }225   226 if (++lineEndIndex == bLen)227 {228 lineStartIndex = currentIndex;229 lineLength = lineEndIndex - currentIndex;230 currentIndex = bLen;231 }232 }233   234 // 处理行235 if (lineStartIndex >= 0)236 {237 // 如果是分界符行238 if (AtBoundaryLine(bytes, lineLength, lineStartIndex, out isLastBoundary))239 {240 // 获取参数值241 if (prevIsParaName)242 {243 paraValue = GetParaValueFromContent(bytes, bLen, prevParaNameLineEndIndex, lineStartIndex, encoding);244 prevIsParaName = false;245 base.Add(paraName, paraValue);246 }247   248 prevIsBoundary = true;249   250 // 最后一行了251 if (isLastBoundary)252 {253 break;254 }255 }256 else257 {258 // 如果上一行是分界符行,则处理本行259 if (prevIsBoundary)260 {261 if (lineLength <= 0)262 {263 continue;264 }265   266 byte[] buffer = new byte[lineLength];267 Array.Copy(bytes, lineStartIndex, buffer, 0, lineLength);268   269 string l = encoding.GetString(buffer);270 int colonIndex = l.IndexOf(':');271 if (colonIndex >= 0)272 {273 string str2 = l.Substring(0, colonIndex);274   275 if (string.Equals(str2, "Content-Disposition", StringComparison.CurrentCultureIgnoreCase))276 {277 // 获取参数名称278 paraName = this.GetParaNameFromContent(l, colonIndex + 1, "name");279   280 //// 获取文件名称281 //string paraFileName = this.GetParaNameFromContent(l, colonIndex + 1, "filename");282   283 // 参数名不为空,且非文件284 if (!string.IsNullOrEmpty(paraName) && !l.Contains("filename"))285 {286 // 标记上一行是参数名称行287 prevIsParaName = true;288   289 // 行结束的索引290 prevParaNameLineEndIndex = lineEndIndex;291 }292 }293 }294 }295   296 prevIsBoundary = false;297 }298 }299   300 // 处理下一行301 lineEndIndex++;302 i = lineEndIndex;303 }304 }305   306 /// <summary>307 /// 获取HTTP实体主体的内容的字节数组形式308 /// </summary>309 /// <param name="stream"></param>310 /// <param name="bufferLen"></param>311 /// <returns></returns>312 private byte[] GetEntityBody(System.IO.Stream stream, int bufferLen)313 {314 // 如果指定的无效长度的缓冲区,则指定一个默认的长度作为缓存大小315 if (bufferLen < 1)316 {317 bufferLen = 0x8000;318 }319   320 // 初始化一个缓存区321 byte[] buffer = new byte[bufferLen];322   323 // 已读取的字节数324 int read = 0;325   326 // 缓冲区中的总字节数327 int block;328   329 // 每次从流中读取缓存大小的数据,直到读取完所有的流为止330 // 如果当前可用的字节数没有请求的字节数那么多,则总字节数可能小于请求的字节数;如果已到达流的末尾,则为零 (0)331 while ((block = stream.Read(buffer, read, buffer.Length - read)) > 0)332 {333 // 重新设定读取位置334 read += block;335   336 // 检查已读取字节数是否到达了缓冲区的边界337 if (read == buffer.Length)338 {339 // 尝试读取一个字节,检查是否还有可以读取的信息340 int nextByte = stream.ReadByte();341   342 // 读取失败则说明读取完成可以返回结果343 if (nextByte == -1)344 {345 return buffer;346 }347   348 // 调整数组大小准备继续读取349 byte[] newBuf = new byte[buffer.Length * 2];350 Array.Copy(buffer, newBuf, buffer.Length);351 newBuf[read] = (byte)nextByte;352   353 // buffer是一个引用(指针),这里意在重新设定buffer指针指向一个更大的内存354 buffer = newBuf;355 read++;356 }357 }358   359 // 如果缓存太大则收缩前面while读取的buffer,然后直接返回360 byte[] ret = new byte[read];361 Array.Copy(buffer, ret, read);362 return ret;363 }364   365 /// <summary>366 /// 获取边界字符串367 /// </summary>368 /// <returns></returns>369 private bool GetMultipartBoundary()370 {371 // 获取边界字符串属性的值372 string attributeFromHeader = GetAttributeFromHeader(contentType, "boundary");373 if (attributeFromHeader == null)374 {375 return false;376 }377   378 // 每一个边界符前面都需要加2个连字符“--”379 attributeFromHeader = "--" + attributeFromHeader;380 boundary = Encoding.ASCII.GetBytes(attributeFromHeader.ToCharArray());381   382 return true;383 }384   385 /// <summary>386 /// 判断是否在分界符行387 /// </summary>388 /// <param name="bytes"></param>389 /// <param name="lineLength"></param>390 /// <param name="lineStartIndex"></param>391 /// <param name="isLastBoundary"></param>392 /// <returns></returns>393 private bool AtBoundaryLine(byte[] bytes, int lineLength, int lineStartIndex, out bool isLastBoundary)394 {395 isLastBoundary = false;396   397 int length = this.boundary.Length;398 if (lineLength != length && lineLength != (length + 2))399 {400 return false;401 }402   403 for (int i = 0; i < length; i++)404 {405 if (bytes[lineStartIndex + i] != this.boundary[i])406 {407 return false;408 }409 }410   411 // 最后一个分界符后两个字符是“--”412 if (lineLength != length)413 {414 if ((bytes[lineStartIndex + length] != 0x2d) || (bytes[(lineStartIndex + length) + 1] != 0x2d))415 {416 return false;417 }418   419 isLastBoundary = true;420 }421   422 return true;423 }424   425 /// <summary>426 /// 获取ContentType中属性的值427 /// </summary>428 /// <param name="headerValue">ContentType</param>429 /// <param name="attrName">属性名称</param>430 /// <returns></returns>431 private string GetAttributeFromHeader(string headerValue, string attrName)432 {433 int index;434 if (headerValue == null)435 {436 return null;437 }438   439 int headerValueLen = headerValue.Length;440 int attrNameLen = attrName.Length;441   442 // 获取attrName的起始索引位置443 int startIndex = 1;444 while (startIndex < headerValueLen)445 {446 // ContentType结构类似:multipart/form-data; boundary=---------------------------7db2693010b2a447 startIndex = CultureInfo.InvariantCulture.CompareInfo.IndexOf(headerValue, attrName, startIndex, CompareOptions.IgnoreCase);448   449 // 不包含“attrName”,跳出循环450 if ((startIndex < 0) || (startIndex + attrNameLen >= headerValueLen))451 {452 break;453 }454   455 // 符合如下条件即跳出循环456 // attrName前一个字符可以为 ; 或 , 或 空白(char 11 12 13)457 // attrName后一个字符可以为 = 或 空白(char 11 12 13)458 char c = headerValue[startIndex - 1];459 char ch2 = headerValue[startIndex + attrNameLen];460 if ((c == ';' || c == ',' || char.IsWhiteSpace(c)) && ((ch2 == '=') || char.IsWhiteSpace(ch2)))461 {462 break;463 }464   465 // 不符合条件,索引前进,继续查找466 startIndex += attrNameLen;467 }468   469 // 不包含符合条件的“attrName”470 if ((startIndex < 0) || (startIndex >= headerValueLen))471 {472 return null;473 }474   475 // ContentType中包含了attrName,获取attrName的值476 startIndex += attrNameLen;477   478 // 如果startIndex是空白,则索引++,直到非空白479 while ((startIndex < headerValueLen) && char.IsWhiteSpace(headerValue[startIndex]))480 {481 startIndex++;482 }483   484 // 移动到符号 =485 if ((startIndex >= headerValueLen) || (headerValue[startIndex] != '='))486 {487 return null;488 }489   490 // 继续前进到值491 startIndex++;492   493 while ((startIndex < headerValueLen) && char.IsWhiteSpace(headerValue[startIndex]))494 {495 startIndex++;496 }497   498 // 如果索引超出,则返回499 if (startIndex >= headerValueLen)500 {501 return null;502 }503   504 // 如果是被双引号包含的505 if ((startIndex < headerValueLen) && (headerValue[startIndex] == '"'))506 {507 if (startIndex == (headerValueLen - 1))508 {509 return null;510 }511   512 // 获取结束的双引号513 index = headerValue.IndexOf('"', startIndex + 1);514   515 if ((index < 0) || (index == (startIndex + 1)))516 {517 return null;518 }519   520 // 截取双引号之间的值521 return headerValue.Substring(startIndex + 1, (index - startIndex) - 1).Trim();522 }523   524 // 索引前进,查找空格或逗号等分隔符525 // 如果找不到,索引到倒数第二个字符526 index = startIndex;527 while (index < headerValueLen)528 {529 if ((headerValue[index] == ' ') || (headerValue[index] == ','))530 {531 break;532 }533   534 index++;535 }536   537 if (index == startIndex)538 {539 return null;540 }541   542 // 截取返回543 return headerValue.Substring(startIndex, index - startIndex).Trim();544 }545   546 /// <summary>547 /// 获取参数名称548 /// </summary>549 /// <param name="l"></param>550 /// <param name="pos"></param>551 /// <param name="name"></param>552 /// <returns></returns>553 private string GetParaNameFromContent(string l, int pos, string name)554 {555 string str = name + "=\"";556 int startIndex = CultureInfo.InvariantCulture.CompareInfo.IndexOf(l, str, pos, CompareOptions.IgnoreCase);557 if (startIndex < 0)558 {559 return null;560 }561 startIndex += str.Length;562 int index = l.IndexOf('"', startIndex);563 if (index < 0)564 {565 return null;566 }567 if (index == startIndex)568 {569 return string.Empty;570 }571   572 return l.Substring(startIndex, index - startIndex);573 }574   575 /// <summary>576 /// 获取参数值577 /// </summary>578 /// <param name="bytes"></param>579 /// <param name="bLen"></param>580 /// <param name="pos"></param>581 /// <param name="lineStartIndex"></param>582 /// <param name="encoding"></param>583 /// <returns></returns>584 private string GetParaValueFromContent(byte[] bytes, int bLen, int pos, int lineStartIndex, Encoding encoding)585 {586 int valueStart = pos + 3;587 int valueLength = -1;588 int valueEndIndex = lineStartIndex - 1;589   590 if (valueStart > bLen || valueEndIndex > bLen)591 {592 return null;593 }594   595 if (bytes[valueEndIndex] == 10)596 {597 valueEndIndex--;598 }599 if (bytes[valueEndIndex] == 13)600 {601 valueEndIndex--;602 }603   604 valueLength = (valueEndIndex - valueStart) + 1;605   606 return encoding.GetString(bytes, valueStart, valueLength);607 }608 }609 }610 注意我在这个类中剔除了获取文件项的方法,只能获取其它表单项的值。611 使用的时候可以如下调用:612 613 查看源代码614 打印?615 /// <summary>616 /// 获取以Post方式提交的参数集合。617 /// </summary>618 /// <param name="isUrlDecode">是否要进行Url解码</param>619 /// <param name="encoding">Url解码时用的编码</param>620 /// <returns>参数集合。</returns>621 /// <example>622 /// string paras = string.Empty;623 /// System.Collections.Specialized.NameValueCollection paraCollection = RequestHelper.GetFormStrings(Encoding.UTF8);624 ///625 /// foreach (string key in paraCollection.AllKeys)626 /// {627 ///     paras += key + ":" + paraCollection[key] + "\r\n";628 /// }629 /// </example>630 public static NameValueCollection GetFormStrings(Encoding encoding)631 {632 return new PostVariableCollection(encoding);633 }
复制代码
0 0
原创粉丝点击