转自: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 else 101 { 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 else 148 { 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 else 257 { 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=---------------------------7db2693010b2a 447 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 }