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

转自: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 }
复制代码

转自: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 }
复制代码
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值