轉自: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 }