关于获取Get和Post请求的参数,.net类库提供了相关的方法:
Request.QueryString 常见的获取Url参数。
Request.Form 常见的获取提交表单项。
这两个方法在获取的时候都会进行解码,并且不是使用者可以控制的。这样可能就会导致一些问题,比如不想对获取到的参数进行解码,或者解码的时候需要特殊的编码而不是系统默认的编码。当然也可以从请求的数据流中读取字节进行解析,这样就增加了处理的复杂度,并且不便于快速多处应用。
这篇文章将提供一些方法,在获取参数的时候指定是否解码以及编码的类型。
一、首先看Url方式提交的参数
/// <param name="isUrlDecode">是否要进行Url解码</param> |
/// <param name="encoding">Url解码时用的编码</param> |
/// <returns>参数集合。</returns> |
/// string paras = string.Empty; |
/// System.Collections.Specialized.NameValueCollection paraCollection = RequestHelper.GetQueryStrings(true, Encoding.UTF8); |
/// foreach (string key in paraCollection.AllKeys) |
/// paras += key + ":" + paraCollection[key] + "\r\n"; |
public static NameValueCollection GetQueryStrings( bool isUrlDecode, Encoding encoding) |
string query = HttpContext.Current.Request.Url.Query; |
if (query.StartsWith( "?" )) |
query = query.Substring(1, query.Length - 1); |
NameValueCollection collection = FillFromString(query, isUrlDecode, encoding); |
/// <param name="s">参数字符串</param> |
/// <param name="isUrlDecode">是否要进行Url解码</param> |
/// <param name="encoding">Url解码时用的编码</param> |
/// <returns>Url参数集合</returns> |
private static NameValueCollection FillFromString( string s, bool isUrlDecode, Encoding encoding) |
NameValueCollection parametersCollection = new NameValueCollection(); |
int sLen = (s != null ) ? s.Length : 0; |
for ( int i = 0; i < sLen; i++) |
string parameterName = null ; |
string parameterValue = null ; |
parameterName = s.Substring(startIndex, endIndex - startIndex); |
parameterValue = s.Substring(endIndex + 1, (i - endIndex) - 1); |
parameterValue = s.Substring(startIndex, i - startIndex); |
parametersCollection.Add(HttpUtility.UrlDecode(parameterName, encoding), HttpUtility.UrlDecode(parameterValue, encoding)); |
parametersCollection.Add(parameterName, parameterValue); |
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.Collections.Generic; |
using System.Collections.Specialized; |
using System.Globalization; |
internal class PostVariableCollection : NameValueCollection |
private string contentType = string .Empty; |
/// 初始化类 PostVariableCollection 的一个新实例 |
public PostVariableCollection() |
FillFormStream(Encoding.Default); |
/// 初始化类 PostVariableCollection 的一个新实例 |
/// <param name="isUrlDecode">是否进行Url解码</param> |
/// <param name="encoding">编码类型</param> |
public PostVariableCollection(Encoding encoding) |
FillFormStream(encoding); |
/// <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; |
byte [] bytes = GetEntityBody(entityStream, 0); |
if (bytes == null || bytes.Length <= 0) |
if (contentType.StartsWith( "application/x-www-form-urlencoded" , System.StringComparison.CurrentCultureIgnoreCase)) |
FillFromBytes(bytes, encoding); |
throw new HttpException( "Invalid_urlencoded_form_data" , ex); |
if (contentType.StartsWith( "multipart/form-data" , System.StringComparison.CurrentCultureIgnoreCase)) |
if (GetMultipartBoundary()) |
FillFromMultipartBytes(bytes, encoding); |
throw new HttpException( "Invalid_multipart_form_data" , ex); |
/// <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++) |
parameterName = HttpUtility.UrlDecode(bytes, startIndex, endIndex - startIndex, encoding); |
parameterValue = HttpUtility.UrlDecode(bytes, endIndex + 1, (i - endIndex) - 1, encoding); |
parameterValue = HttpUtility.UrlDecode(bytes, startIndex, i - startIndex, encoding); |
base .Add(parameterName, parameterValue); |
if ((i == (bLen - 1)) && (bytes[i] == 0x26)) |
base .Add( null , string .Empty); |
/// 从多部件的实体主体内容中读取变量填充到集合,文件排除在外。 |
/// <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 lineEndIndex = currentIndex; |
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++) |
while (lineEndIndex < bLen) |
if (bytes[lineEndIndex] == 10) |
lineStartIndex = currentIndex; |
lineLength = lineEndIndex - currentIndex; |
if (lineLength > 0 && bytes[lineEndIndex - 1] == 13) |
currentIndex = lineEndIndex + 1; |
if (++lineEndIndex == bLen) |
lineStartIndex = currentIndex; |
lineLength = lineEndIndex - currentIndex; |
if (AtBoundaryLine(bytes, lineLength, lineStartIndex, out isLastBoundary)) |
paraValue = GetParaValueFromContent(bytes, bLen, prevParaNameLineEndIndex, lineStartIndex, encoding); |
base .Add(paraName, paraValue); |
byte [] buffer = new byte [lineLength]; |
Array.Copy(bytes, lineStartIndex, buffer, 0, lineLength); |
string l = encoding.GetString(buffer); |
int colonIndex = l.IndexOf( ':' ); |
string str2 = l.Substring(0, colonIndex); |
if ( string .Equals(str2, "Content-Disposition" , StringComparison.CurrentCultureIgnoreCase)) |
paraName = this .GetParaNameFromContent(l, colonIndex + 1, "name" ); |
if (! string .IsNullOrEmpty(paraName) && !l.Contains( "filename" )) |
prevParaNameLineEndIndex = lineEndIndex; |
/// <param name="stream"></param> |
/// <param name="bufferLen"></param> |
private byte [] GetEntityBody(System.IO.Stream stream, int bufferLen) |
byte [] buffer = new byte [bufferLen]; |
while ((block = stream.Read(buffer, read, buffer.Length - read)) > 0) |
if (read == buffer.Length) |
int nextByte = stream.ReadByte(); |
byte [] newBuf = new byte [buffer.Length * 2]; |
Array.Copy(buffer, newBuf, buffer.Length); |
newBuf[read] = ( byte )nextByte; |
byte [] ret = new byte [read]; |
Array.Copy(buffer, ret, read); |
private bool GetMultipartBoundary() |
string attributeFromHeader = GetAttributeFromHeader(contentType, "boundary" ); |
if (attributeFromHeader == null ) |
attributeFromHeader = "--" + attributeFromHeader; |
boundary = Encoding.ASCII.GetBytes(attributeFromHeader.ToCharArray()); |
/// <param name="bytes"></param> |
/// <param name="lineLength"></param> |
/// <param name="lineStartIndex"></param> |
/// <param name="isLastBoundary"></param> |
private bool AtBoundaryLine( byte [] bytes, int lineLength, int lineStartIndex, out bool isLastBoundary) |
int length = this .boundary.Length; |
if (lineLength != length && lineLength != (length + 2)) |
for ( int i = 0; i < length; i++) |
if (bytes[lineStartIndex + i] != this .boundary[i]) |
if (lineLength != length) |
if ((bytes[lineStartIndex + length] != 0x2d) || (bytes[(lineStartIndex + length) + 1] != 0x2d)) |
/// <param name="headerValue">ContentType</param> |
/// <param name="attrName">属性名称</param> |
private string GetAttributeFromHeader( string headerValue, string attrName) |
int headerValueLen = headerValue.Length; |
int attrNameLen = attrName.Length; |
while (startIndex < headerValueLen) |
startIndex = CultureInfo.InvariantCulture.CompareInfo.IndexOf(headerValue, attrName, startIndex, CompareOptions.IgnoreCase); |
if ((startIndex < 0) || (startIndex + attrNameLen >= headerValueLen)) |
char c = headerValue[startIndex - 1]; |
char ch2 = headerValue[startIndex + attrNameLen]; |
if ((c == ';' || c == ',' || char .IsWhiteSpace(c)) && ((ch2 == '=' ) || char .IsWhiteSpace(ch2))) |
startIndex += attrNameLen; |
if ((startIndex < 0) || (startIndex >= headerValueLen)) |
startIndex += attrNameLen; |
while ((startIndex < headerValueLen) && char .IsWhiteSpace(headerValue[startIndex])) |
if ((startIndex >= headerValueLen) || (headerValue[startIndex] != '=' )) |
while ((startIndex < headerValueLen) && char .IsWhiteSpace(headerValue[startIndex])) |
if (startIndex >= headerValueLen) |
if ((startIndex < headerValueLen) && (headerValue[startIndex] == '"' )) |
if (startIndex == (headerValueLen - 1)) |
index = headerValue.IndexOf( '"' , startIndex + 1); |
if ((index < 0) || (index == (startIndex + 1))) |
return headerValue.Substring(startIndex + 1, (index - startIndex) - 1).Trim(); |
while (index < headerValueLen) |
if ((headerValue[index] == ' ' ) || (headerValue[index] == ',' )) |
return headerValue.Substring(startIndex, index - startIndex).Trim(); |
/// <param name="l"></param> |
/// <param name="pos"></param> |
/// <param name="name"></param> |
private string GetParaNameFromContent( string l, int pos, string name) |
string str = name + "=\"" ; |
int startIndex = CultureInfo.InvariantCulture.CompareInfo.IndexOf(l, str, pos, CompareOptions.IgnoreCase); |
startIndex += str.Length; |
int index = l.IndexOf( '"' , startIndex); |
return l.Substring(startIndex, index - startIndex); |
/// <param name="bytes"></param> |
/// <param name="bLen"></param> |
/// <param name="pos"></param> |
/// <param name="lineStartIndex"></param> |
/// <param name="encoding"></param> |
private string GetParaValueFromContent( byte [] bytes, int bLen, int pos, int lineStartIndex, Encoding encoding) |
int valueStart = pos + 3; |
int valueEndIndex = lineStartIndex - 1; |
if (valueStart > bLen || valueEndIndex > bLen) |
if (bytes[valueEndIndex] == 10) |
if (bytes[valueEndIndex] == 13) |
valueLength = (valueEndIndex - valueStart) + 1; |
return encoding.GetString(bytes, valueStart, valueLength); |
注意我在这个类中剔除了获取文件项的方法,只能获取其它表单项的值。
使用的时候可以如下调用:
/// <param name="isUrlDecode">是否要进行Url解码</param> |
/// <param name="encoding">Url解码时用的编码</param> |
/// <returns>参数集合。</returns> |
/// string paras = string.Empty; |
/// System.Collections.Specialized.NameValueCollection paraCollection = RequestHelper.GetFormStrings(Encoding.UTF8); |
/// foreach (string key in paraCollection.AllKeys) |
/// paras += key + ":" + paraCollection[key] + "\r\n"; |
public static NameValueCollection GetFormStrings(Encoding encoding) |
return new PostVariableCollection(encoding); |