2013年5月30日星期四

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

asp.net获取Get和Post请求的参数
2012-03-17 10:15:34 来源: 作者: 【 】 浏览:700次 评论:0
关于获取Get和Post请求的参数,。net类库提供了相关的方法:   Request.QueryString 常见的获取Url参数。
  Request.Form 常见的获取提交表单项。
  这两个方法在获取的时候都会进行解码,并且不是使用者可以控制的。这样可能就会导致一些问题,比如不想对获取到的参数进行解码,或者解码的时候需要特 殊的编码而不是系统默认的编码。当然也可以从请求的数据流中读取字节进行解析,这样就增加了处理的复杂度,并且不便于快速多处应用。
  这篇文章将提供一些方法,在获取参数的时候指定是否解码以及编码的类型。
  一、首先看Url方式提交的参数
  查看源代码打印?
  /// <summary> /// 获取以Url方式提交的参数集合。
  /// </summary> /// <param name="isUrlDecode">是否要进行Url解码</param> /// <param name="encoding">Url解码时用的编码</param> /// <returns>参数集合。</returns> /// <example> /// string paras = string.Empty;/// System.Collections.Specialized.Nameva lueCollection paraCollection = RequestHelper.GetQueryStrings(true, Encoding.UTF8);/// /// foreach (string key in paraCollection.AllKeys)
  /// { /// paras += key + ":" + paraCollection[key] + "\r\n";/// } /// </example> public static Nameva lueCollection GetQueryStrings(bool isUrlDecode, Encoding encoding)
  { // 获取查询字符串string query = HttpContext.Current.Request.Url.Query;if (query.StartsWith("?"))
  { if (query.Length > 1)
  { query = query.Substring(1, query.Length - 1);} else { query = string.Empty;}
  // 处理查询字符串Nameva lueCollection collection = FillFromString(query, isUrlDecode, encoding);return collection;}
  /// <summary> /// 从参数字符串获取参数集合/// </summary> /// <param name="s">参数字符串</param> /// <param name="isUrlDecode">是否要进行Url解码</param> /// <param name="encoding">Url解码时用的编码</param> /// <returns>Url参数集合</returns> private static Nameva lueCollection FillFromString(string s, bool isUrlDecode, Encoding encoding)
  { Nameva lueCollection parametersCollection = new Nameva lueCollection();
  // 参数字符串长度int sLen = (s != null) ? s.Length : 0;
  // 遍历每个字符for (int i = 0; i < sLen; i++)
  { // 参数名开始位置int startIndex = i;
  // 参数名结束位置int endIndex = -1;
  // 字符索引前进,直到遇到等号,更新结束的索引位置// 如果遇到&符号,则参数结束,退出循环while (i < sLen)
  { char ch = s[i];if (ch == '=')
  { if (endIndex < 0)
  { endIndex = i;} else if (ch == '&')
  { break;}
  // 字符索引前进i++;}
  string parameterName = null;string parameterValue = null;
  // 存在等号,可以获取到参数名和参数值if (endIndex >= 0)
  { parameterName = s.Substring(startIndex, endIndex - startIndex);parameterValue = s.Substring(endIndex + 1, (i - endIndex) - 1);} else { parameterValue = s.Substring(startIndex, i - startIndex);}
  // 需要解码if (isUrlDecode)
  { parametersCollection.Add(HttpUtility.UrlDecode(parameterName, encoding), HttpUtility.UrlDecode(parameterValue, encoding));} else { parametersCollection.Add(parameterName, parameterValue);}
  // 最后一个字符是 &,则添加一个参数为null的Nameva lue对。
  if ((i == (sLen - 1)) && (s[i] == '&'))
  { parametersCollection.Add(null, string.Empty);}
  return parametersCollection;}
  FillFromString方法是通过反编译微软的类库,然后经过简单修改而来的。处理方式严谨可靠,学习了下。
  使用的时候调用GetQueryStrings方法获取全部Get参数的集合。
  二、获取Post方式提交的参数
  相比获取通过Url方式提交的参数,获取通过Post方式提交的参数要复杂一些。
  要区分两种表单的类型:application/x-www-form-urlencoded 和 multipart/form-data,前者只能提交一般参数,后者还可以提交文件。
  因为通过这种方式提交的数据,是先从流中读取字节数据,然后解码的,所以解码是必须的,但是可以提供特殊的编码类型。
  我这里专门定义了一个类来解析这些数据,当然这个方法也是按照微软的思路来做的。
  查看源代码打印?
  using System;using System.Collections.Generic;using System.Collections.Specialized;using System.Linq;using System.Text;using System.Web;using System.Globalization;
  namespace VeryCodes.Web { /// <summary> /// 以Post方式提交的变量的集合。
  /// </summary> /// <remarks> /// 不包含提交的文件。
  /// </remarks> internal class PostVariableCollection : Nameva lueCollection { /// <summary> /// Content Type /// </summary> private string contentType = string.Empty;
  /// <summary> /// 分界符/// </summary> private byte[] boundary;
  /// <summary> /// 初始化类 PostVariableCollection 的一个新实例/// </summary> public PostVariableCollection()
  { FillFormStream(Encoding.Default);}
  /// <summary> /// 初始化类 PostVariableCollection 的一个新实例/// </summary> /// <param name="isUrlDecode">是否进行Url解码</param> /// <param name="encoding">编码类型</param> public PostVariableCollection(Encoding encoding)
  { FillFormStream(encoding);}
  /// <summary> /// 使用HTTP实体主体内容填充集合/// </summary> /// <param name="isUrlDecode"></param> /// <param name="encoding"></param> private void FillFormStream(Encoding encoding)
  { contentType = HttpContext.Current.Request.ContentType;
  if (!string.IsNullOrEmpty(contentType))
  { System.IO.Stream entityStream = HttpContext.Current.Request.InputStream;
  // 获取HTTP实体主体的内容byte[] bytes = GetEntityBody(entityStream, 0);
  if (bytes == null || bytes.Length <= 0)
  { return;}
  // 因为是字节数据,所有的数据都需要解码if (contentType.StartsWith("application/x-www-form-urlencoded", System.StringComparison.CurrentCultureIgnoreCase))
  { try { FillFromBytes(bytes, encoding);return;} catch (Exception ex)
  { throw new HttpException("Invalid_urlencoded_form_data", ex);}
  if (contentType.StartsWith("multipart/form-data", System.StringComparison.CurrentCultureIgnoreCase))
  { if (GetMultipartBoundary())
  { try { // 获取各个参数FillFromMultipartBytes(bytes, encoding);return;} catch (Exception ex)
  { throw new HttpException("Invalid_multipart_form_data", ex);}
  /// <summary> /// 从字节数组读取变量填充到集合/// </summary> /// <param name="bytes"></param> /// <param name="encoding"></param> private void FillFromBytes(byte[] bytes, Encoding encoding)
  { // 字节数组长度int bLen = (bytes != null) ? bytes.Length : 0;
  // 遍历字节数组for (int i = 0; i < bLen; i++)
  { string parameterName;string parameterValue;
  //参数名开始位置int startIndex = i;
  //参数名结束位置int endIndex = -1;
  while (i < bLen)
  { byte bt = bytes[i];
  // 符号:= if (bt == 0x3d)
  { if (endIndex < 0)
  { endIndex = i;} else if (bt == 0x26) //符号:& { break;} i++;}
  if (endIndex >= 0)
  { parameterName = HttpUtility.UrlDecode(bytes, startIndex, endIndex - startIndex, encoding);parameterValue = HttpUtility.UrlDecode(bytes, endIndex + 1, (i - endIndex) - 1, encoding);} else { parameterName = null;parameterValue = HttpUtility.UrlDecode(bytes, startIndex, i - startIndex, encoding);}
  base.Add(parameterName, parameterValue);
  if ((i == (bLen - 1)) && (bytes[i] == 0x26))
  { base.Add(null, string.Empty);}
  /// <summary> /// 从多部件的实体主体内容中读取变量填充到集合,文件排除在外。
  /// </summary> /// <param name="bytes"></param> /// <param name="isUrlDecode"></param> /// <param name="encoding"></param> private void FillFromMultipartBytes(byte[] bytes, Encoding encoding)
  { // 字节数组长度int bLen = (bytes != null) ? bytes.Length : 0;
  // 当前字节索引int currentIndex = 0;
  // 当前行开始索引int lineStartIndex = -1;
  // 当前行结束索引int lineEndIndex = currentIndex;
  // 当前行字节长度int lineLength = -1;
  // 是否最后一个分界符bool isLastBoundary = false;
  // 上一行是否分界符行bool prevIsBoundary = false;
  // 上一行是否参数名称行bool prevIsParaName = false;
  // 上一行是否参数名称行的结束索引int prevParaNameLineEndIndex = 0;
  // 参数名称string paraName = string.Empty;
  // 参数值string paraValue = string.Empty;
  // 遍历字节数组for (int i = 0; i < bLen; i++)
  { //查找行,行由char(13)+char(10)结束while (lineEndIndex < bLen)
  { // 换行if (bytes[lineEndIndex] == 10)
  { lineStartIndex = currentIndex;lineLength = lineEndIndex - currentIndex;
  // 回车if (lineLength > 0 && bytes[lineEndIndex - 1] == 13)
  { lineLength——;}
  currentIndex = lineEndIndex + 1;
  break;}
  if (++lineEndIndex == bLen)
  { lineStartIndex = currentIndex;lineLength = lineEndIndex - currentIndex;currentIndex = bLen;}
  // 处理行if (lineStartIndex >= 0)
  { // 如果是分界符行if (AtBoundaryLine(bytes, lineLength, lineStartIndex, out isLastBoundary))
  { // 获取参数值if (prevIsParaName)
  { paraValue = GetParaValueFromContent(bytes, bLen, prevParaNameLineEndIndex, lineStartIndex, encoding);prevIsParaName = false;base.Add(paraName, paraValue);}
  prevIsBoundary = true;
  // 最后一行了if (isLastBoundary)
  { break;} else { // 如果上一行是分界符行,则处理本行if (prevIsBoundary)
  { if (lineLength <= 0)
  { continue;}
  byte[] buffer = new byte[lineLength];Array.Copy(bytes, lineStartIndex, buffer, 0, lineLength);
  string l = encoding.GetString(buffer);int colonIndex = l.IndexOf(':');if (colonIndex >= 0)
  { string str2 = l.Substring(0, colonIndex);
  if (string.Equals(str2, "Content-Disposition", StringComparison.CurrentCultureIgnoreCase))
  { // 获取参数名称paraName = this.GetParaNameFromContent(l, colonIndex + 1, "name");
  //// 获取文件名称//string paraFileName = this.GetParaNameFromContent(l, colonIndex + 1, "filename");
  // 参数名不为空,且非文件if (!string.IsNullOrEmpty(paraName) && !l.Contains("filename"))
  { // 标记上一行是参数名称行prevIsParaName = true;
  // 行结束的索引prevParaNameLineEndIndex = lineEndIndex;}
  prevIsBoundary = false;}
  // 处理下一行lineEndIndex++;i = lineEndIndex;}
  /// <summary> /// 获取HTTP实体主体的内容的字节数组形式/// </summary> /// <param name="stream"></param> /// <param name="bufferLen"></param> /// <returns></returns> private byte[] GetEntityBody(System.IO.Stream stream, int bufferLen)
  { // 如果指定的无效长度的缓冲区,则指定一个默认的长度作为缓存大小if (bufferLen < 1)
  { bufferLen = 0x8000;}
  // 初始化一个缓存区byte[] buffer = new byte[bufferLen];
  // 已读取的字节数int read = 0;
  // 缓冲区中的总字节数int block;
  // 每次从流中读取缓存大小的数据,直到读取完所有的流为止// 如果当前可用的字节数没有请求的字节数那么多,则总字节数可能小于请求的字节数;如果已到达流的末尾,则为零 (0)
  while ((block = stream.Read(buffer, read, buffer.Length - read)) > 0)
  { // 重新设定读取位置read += block;
  // 检查已读取字节数是否到达了缓冲区的边界if (read == buffer.Length)
  { // 尝试读取一个字节,检查是否还有可以读取的信息int nextByte = stream.ReadByte();
  // 读取失败则说明读取完成可以返回结果if (nextByte == -1)
  { return buffer;}
  // 调整数组大小准备继续读取byte[] newBuf = new byte[buffer.Length * 2];Array.Copy(buffer, newBuf, buffer.Length);newBuf[read] = (byte)nextByte;
  // buffer是一个引用(指针),这里意在重新设定buffer指针指向一个更大的内存buffer = newBuf;read++;}
  // 如果缓存太大则收缩前面while读取的buffer,然后直接返回byte[] ret = new byte[read];Array.Copy(buffer, ret, read);return ret;}
  /// <summary> /// 获取边界字符串/// </summary> /// <returns></returns> private bool GetMultipartBoundary()
  { // 获取边界字符串属性的值string attributeFromHeader = GetAttributeFromHeader(contentType, "boundary");if (attributeFromHeader == null)
  { return false;}
  // 每一个边界符前面都需要加2个连字符“——”
  attributeFromHeader = "——" + attributeFromHeader;boundary = Encoding.ASCII.GetBytes(attributeFromHeader.ToCharArray());
  return true;}
  /// <summary> /// 判断是否在分界符行/// </summary> /// <param name="bytes"></param> /// <param name="lineLength"></param> /// <param name="lineStartIndex"></param> /// <param name="isLastBoundary"></param> /// <returns></returns> private bool AtBoundaryLine(byte[] bytes, int lineLength, int lineStartIndex, out bool isLastBoundary)
  { isLastBoundary = false;
  int length = this.boundary.Length;if (lineLength != length && lineLength != (length + 2))
  { return false;}
  for (int i = 0; i < length; i++)
  { if (bytes[lineStartIndex + i] != this.boundary[i])
  { return false;}
  // 最后一个分界符后两个字符是“——”
  if (lineLength != length)
  { if ((bytes[lineStartIndex + length] != 0x2d) || (bytes[(lineStartIndex + length) + 1] != 0x2d))
  { return false;}
  isLastBoundary = true;}
  return true;}
  /// <summary> /// 获取ContentType中属性的值/// </summary> /// <param name="headerValue">ContentType</param> /// <param name="attrName">属性名称</param> /// <returns></returns> private string GetAttributeFromHeader(string headerValue, string attrName)
  { int index;if (headerValue == null)
  { return null;}
  int headerValueLen = headerValue.Length;int attrNameLen = attrName.Length;
  // 获取attrName的起始索引位置int startIndex = 1;while (startIndex < headerValueLen)
  { // ContentType结构类似:multipart/form-data; boundary=——7db2693010b2a startIndex = CultureInfo.InvariantCulture.CompareInfo.IndexOf(headerValue, attrName, startIndex, CompareOptions.IgnoreCase);
  // 不包含“attrName”,跳出循环if ((startIndex < 0) || (startIndex + attrNameLen >= headerValueLen))
  { break;}
  // 符合如下条件即跳出循环// attrName前一个字符可以为 ; 或 , 或 空白(char 11 12 13)
  // attrName后一个字符可以为 = 或 空白(char 11 12 13)
  char c = headerValue[startIndex - 1];char ch2 = headerValue[startIndex + attrNameLen];if ((c == ';' || c == ',' || char.IsWhiteSpace(c)) && ((ch2 == '=') || char.IsWhiteSpace(ch2)))
  { break;}
  // 不符合条件,索引前进,继续查找startIndex += attrNameLen;}
  // 不包含符合条件的“attrName”
  if ((startIndex < 0) || (startIndex >= headerValueLen))
  { return null;}
  // ContentType中包含了attrName,获取attrName的值startIndex += attrNameLen;
  // 如果startIndex是空白,则索引++,直到非空白while ((startIndex < headerValueLen) && char.IsWhiteSpace(headerValue[startIndex]))
  { startIndex++;}
  // 移动到符号 = if ((startIndex >= headerValueLen) || (headerValue[startIndex] != '='))
  { return null;}
  // 继续前进到值startIndex++;
  while ((startIndex < headerValueLen) && char.IsWhiteSpace(headerValue[startIndex]))
  { startIndex++;}
  // 如果索引超出,则返回if (startIndex >= headerValueLen)
  { return null;}
  // 如果是被双引号包含的if ((startIndex < headerValueLen) && (headerValue[startIndex] == '"'))
  { if (startIndex == (headerValueLen - 1))
  { return null;}
  // 获取结束的双引号index = headerValue.IndexOf('"', startIndex + 1);
  if ((index < 0) || (index == (startIndex + 1)))
  { return null;}
  // 截取双引号之间的值return headerValue.Substring(startIndex + 1, (index - startIndex) - 1)。Trim();}
  // 索引前进,查找空格或逗号等分隔符// 如果找不到,索引到倒数第二个字符index = startIndex;while (index < headerValueLen)
  { if ((headerValue[index] == ' ') || (headerValue[index] == ','))
  { break;}
  index++;}
  if (index == startIndex)
  { return null;}
  // 截取返回return headerValue.Substring(startIndex, index - startIndex)。Trim();}
  /// <summary> /// 获取参数名称/// </summary> /// <param name="l"></param> /// <param name="pos"></param> /// <param name="name"></param> /// <returns></returns> private string GetParaNameFromContent(string l, int pos, string name)
  { string str = name + "=\"";int startIndex = CultureInfo.InvariantCulture.CompareInfo.IndexOf(l, str, pos, CompareOptions.IgnoreCase);if (startIndex < 0)
  { return null;} startIndex += str.Length;int index = l.IndexOf('"', startIndex);if (index < 0)
  { return null;} if (index == startIndex)
  { return string.Empty;}
  return l.Substring(startIndex, index - startIndex);}
  /// <summary> /// 获取参数值/// </summary> /// <param name="bytes"></param> /// <param name="bLen"></param> /// <param name="pos"></param> /// <param name="lineStartIndex"></param> /// <param name="encoding"></param> /// <returns></returns> private string GetParaValueFromContent(byte[] bytes, int bLen, int pos, int lineStartIndex, Encoding encoding)
  { int valueStart = pos + 3;int valueLength = -1;int valueEndIndex = lineStartIndex - 1;
  if (valueStart > bLen || valueEndIndex > bLen)
  { return null;}
  if (bytes[valueEndIndex] == 10)
  { valueEndIndex——;} if (bytes[valueEndIndex] == 13)
  { valueEndIndex——;}
  valueLength = (valueEndIndex - valueStart) + 1;
  return encoding.GetString(bytes, valueStart, valueLength);}注意我在这个类中剔除了获取文件项的方法,只能获取其它表单项的值。
  使用的时候可以如下调用:
  查看源代码打印?
  /// <summary> /// 获取以Post方式提交的参数集合。
  /// </summary> /// <param name="isUrlDecode">是否要进行Url解码</param> /// <param name="encoding">Url解码时用的编码</param> /// <returns>参数集合。</returns> /// <example> /// string paras = string.Empty;/// System.Collections.Specialized.Nameva lueCollection paraCollection = RequestHelper.GetFormStrings(Encoding.UTF8);/// /// foreach (string key in paraCollection.AllKeys)
  /// { /// paras += key + ":" + paraCollection[key] + "\r\n";/// } /// </example> public static Nameva lueCollection GetFormStrings(Encoding encoding)
  { return new PostVariableCollection(encoding);}

1 条评论: