您的位置:首页 > 编程语言 > ASP

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

2015-06-26 13:38 746 查看
转自: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 }


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: