HttpRequest的QueryString屬性 的一點(diǎn)認(rèn)識(shí)
更新時(shí)間:2012年11月06日 17:03:38 作者:
我們開(kāi)發(fā)asp.net程序獲取QueryString時(shí),經(jīng)常性的遇到一些url編碼問(wèn)題
如:

當(dāng)然我們一般都是按照提示來(lái)把framework版本設(shè)置2.0來(lái)解決。為什么可以這么解決了,還有沒(méi)有其它的解決方法了。
先讓我們看看QueryString的源代碼吧:
public NameValueCollection QueryString
{
get
{
if (this._queryString == null)
{
this._queryString = new HttpValueCollection();
if (this._wr != null)
{
this.FillInQueryStringCollection();
}
this._queryString.MakeReadOnly();
}
if (this._flags[1])
{
this._flags.Clear(1);
this.ValidateNameValueCollection(this._queryString, RequestValidationSource.QueryString);
}
return this._queryString;
}
}
private void FillInQueryStringCollection()
{
byte[] queryStringBytes = this.QueryStringBytes;
if (queryStringBytes != null)
{
if (queryStringBytes.Length != 0)
{
this._queryString.FillFromEncodedBytes(queryStringBytes, this.QueryStringEncoding);
}
}
else if (!string.IsNullOrEmpty(this.QueryStringText))
{
this._queryString.FillFromString(this.QueryStringText, true, this.QueryStringEncoding);
}
}
先讓我們插入一點(diǎn) 那就是QueryString默認(rèn)已經(jīng)做了url解碼。 其中HttpValueCollection的 FillFromEncodedBytes方法如下
internal void FillFromEncodedBytes(byte[] bytes, Encoding encoding)
{
int num = (bytes != null) ? bytes.Length : 0;
for (int i = 0; i < num; i++)
{
string str;
string str2;
this.ThrowIfMaxHttpCollectionKeysExceeded();
int offset = i;
int num4 = -1;
while (i < num)
{
byte num5 = bytes[i];
if (num5 == 0x3d)
{
if (num4 < 0)
{
num4 = i;
}
}
else if (num5 == 0x26)
{
break;
}
i++;
}
if (num4 >= 0)
{
str = HttpUtility.UrlDecode(bytes, offset, num4 - offset, encoding);
str2 = HttpUtility.UrlDecode(bytes, num4 + 1, (i - num4) - 1, encoding);
}
else
{
str = null;
str2 = HttpUtility.UrlDecode(bytes, offset, i - offset, encoding);
}
base.Add(str, str2);
if ((i == (num - 1)) && (bytes[i] == 0x26))
{
base.Add(null, string.Empty);
}
}
}
從這里我們可以看到QueryString已經(jīng)為我們做了解碼工作,我們不需要寫成 HttpUtility.HtmlDecode(Request.QueryString["xxx"])而是直接寫成Request.QueryString["xxx"]就ok了。
現(xiàn)在讓我們來(lái)看看你QueryString的驗(yàn)證,在代碼中有
if (this._flags[1])
{
this._flags.Clear(1);
this.ValidateNameValueCollection(this._queryString, RequestValidationSource.QueryString);
}
一看this.ValidateNameValueCollection這個(gè)方法名稱就知道是干什么的了,驗(yàn)證QueryString數(shù)據(jù);那么在什么情況下驗(yàn)證的了?
讓我們看看this._flags[1]在什么地方設(shè)置的:
public void ValidateInput()
{
if (!this._flags[0x8000])
{
this._flags.Set(0x8000);
this._flags.Set(1);
this._flags.Set(2);
this._flags.Set(4);
this._flags.Set(0x40);
this._flags.Set(0x80);
this._flags.Set(0x100);
this._flags.Set(0x200);
this._flags.Set(8);
}
}
而該方法在ValidateInputIfRequiredByConfig中調(diào)用,調(diào)用代碼
internal void ValidateInputIfRequiredByConfig()
{
.........
if (httpRuntime.RequestValidationMode >= VersionUtil.Framework40)
{
this.ValidateInput();
}
}
我想現(xiàn)在大家都應(yīng)該明白為什么錯(cuò)題提示讓我們把framework改為2.0了吧。應(yīng)為在4.0后才驗(yàn)證。這種解決問(wèn)題的方法是關(guān)閉驗(yàn)證,那么我們是否可以改變默認(rèn)的驗(yàn)證規(guī)則了?
讓我們看看ValidateNameValueCollection
private void ValidateNameValueCollection(NameValueCollection nvc, RequestValidationSource requestCollection)
{
int count = nvc.Count;
for (int i = 0; i < count; i++)
{
string key = nvc.GetKey(i);
if ((key == null) || !key.StartsWith("__", StringComparison.Ordinal))
{
string str2 = nvc.Get(i);
if (!string.IsNullOrEmpty(str2))
{
this.ValidateString(str2, key, requestCollection);
}
}
}
}
private void ValidateString(string value, string collectionKey, RequestValidationSource requestCollection)
{
int num;
value = RemoveNullCharacters(value);
if (!RequestValidator.Current.IsValidRequestString(this.Context, value, requestCollection, collectionKey, out num))
{
string str = collectionKey + "=\"";
int startIndex = num - 10;
if (startIndex <= 0)
{
startIndex = 0;
}
else
{
str = str + "...";
}
int length = num + 20;
if (length >= value.Length)
{
length = value.Length;
str = str + value.Substring(startIndex, length - startIndex) + "\"";
}
else
{
str = str + value.Substring(startIndex, length - startIndex) + "...\"";
}
string requestValidationSourceName = GetRequestValidationSourceName(requestCollection);
throw new HttpRequestValidationException(SR.GetString("Dangerous_input_detected", new object[] { requestValidationSourceName, str }));
}
}
哦?原來(lái)一切都明白了,驗(yàn)證是在RequestValidator做的。
public class RequestValidator
{
// Fields
private static RequestValidator _customValidator;
private static readonly Lazy<RequestValidator> _customValidatorResolver = new Lazy<RequestValidator>(new Func<RequestValidator>(RequestValidator.GetCustomValidatorFromConfig));
// Methods
private static RequestValidator GetCustomValidatorFromConfig()
{
HttpRuntimeSection httpRuntime = RuntimeConfig.GetAppConfig().HttpRuntime;
Type userBaseType = ConfigUtil.GetType(httpRuntime.RequestValidationType, "requestValidationType", httpRuntime);
ConfigUtil.CheckBaseType(typeof(RequestValidator), userBaseType, "requestValidationType", httpRuntime);
return (RequestValidator) HttpRuntime.CreatePublicInstance(userBaseType);
}
internal static void InitializeOnFirstRequest()
{
RequestValidator local1 = _customValidatorResolver.Value;
}
private static bool IsAtoZ(char c)
{
return (((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')));
}
protected internal virtual bool IsValidRequestString(HttpContext context, string value, RequestValidationSource requestValidationSource, string collectionKey, out int validationFailureIndex)
{
if (requestValidationSource == RequestValidationSource.Headers)
{
validationFailureIndex = 0;
return true;
}
return !CrossSiteScriptingValidation.IsDangerousString(value, out validationFailureIndex);
}
// Properties
public static RequestValidator Current
{
get
{
if (_customValidator == null)
{
_customValidator = _customValidatorResolver.Value;
}
return _customValidator;
}
set
{
if (value == null)
{
throw new ArgumentNullException("value");
}
_customValidator = value;
}
}
}
主要的驗(yàn)證方法還是在CrossSiteScriptingValidation.IsDangerousString(value, out validationFailureIndex);而CrossSiteScriptingValidation是一個(gè)內(nèi)部類,無(wú)法修改。
讓我們看看CrossSiteScriptingValidation類大代碼把
internal static class CrossSiteScriptingValidation
{
// Fields
private static char[] startingChars = new char[] { '<', '&' };
// Methods
private static bool IsAtoZ(char c)
{
return (((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')));
}
internal static bool IsDangerousString(string s, out int matchIndex)
{
matchIndex = 0;
int startIndex = 0;
while (true)
{
int num2 = s.IndexOfAny(startingChars, startIndex);
if (num2 < 0)
{
return false;
}
if (num2 == (s.Length - 1))
{
return false;
}
matchIndex = num2;
char ch = s[num2];
if (ch != '&')
{
if ((ch == '<') && ((IsAtoZ(s[num2 + 1]) || (s[num2 + 1] == '!')) || ((s[num2 + 1] == '/') || (s[num2 + 1] == '?'))))
{
return true;
}
}
else if (s[num2 + 1] == '#')
{
return true;
}
startIndex = num2 + 1;
}
}
internal static bool IsDangerousUrl(string s)
{
if (string.IsNullOrEmpty(s))
{
return false;
}
s = s.Trim();
int length = s.Length;
if (((((length > 4) && ((s[0] == 'h') || (s[0] == 'H'))) && ((s[1] == 't') || (s[1] == 'T'))) && (((s[2] == 't') || (s[2] == 'T')) && ((s[3] == 'p') || (s[3] == 'P')))) && ((s[4] == ':') || (((length > 5) && ((s[4] == 's') || (s[4] == 'S'))) && (s[5] == ':'))))
{
return false;
}
if (s.IndexOf(':') == -1)
{
return false;
}
return true;
}
internal static bool IsValidJavascriptId(string id)
{
if (!string.IsNullOrEmpty(id))
{
return CodeGenerator.IsValidLanguageIndependentIdentifier(id);
}
return true;
}
}
結(jié)果我們發(fā)現(xiàn)&# <! </ <? <[a-zA-z] 這些情況驗(yàn)證都是通不過(guò)的。
所以我們只需要重寫RequestValidator就可以了。
例如我們現(xiàn)在需要處理我們現(xiàn)在需要過(guò)濾QueryString中k=&...的情況
public class CustRequestValidator : RequestValidator
{
protected override bool IsValidRequestString(HttpContext context, string value, RequestValidationSource requestValidationSource, string collectionKey, out int validationFailureIndex)
{
validationFailureIndex = 0;
//我們現(xiàn)在需要過(guò)濾QueryString中k=&...的情況
if (requestValidationSource == RequestValidationSource.QueryString&&collectionKey.Equals("k")&& value.StartsWith("&"))
{
return true;
}
return base.IsValidRequestString(context, value, requestValidationSource, collectionKey, out validationFailureIndex);
}
}
<httpRuntime requestValidationType="MvcApp.CustRequestValidator"/>
個(gè)人在這里只是提供一個(gè)思想,歡迎大家拍磚!

當(dāng)然我們一般都是按照提示來(lái)把framework版本設(shè)置2.0來(lái)解決。為什么可以這么解決了,還有沒(méi)有其它的解決方法了。
先讓我們看看QueryString的源代碼吧:
復(fù)制代碼 代碼如下:
public NameValueCollection QueryString
{
get
{
if (this._queryString == null)
{
this._queryString = new HttpValueCollection();
if (this._wr != null)
{
this.FillInQueryStringCollection();
}
this._queryString.MakeReadOnly();
}
if (this._flags[1])
{
this._flags.Clear(1);
this.ValidateNameValueCollection(this._queryString, RequestValidationSource.QueryString);
}
return this._queryString;
}
}
private void FillInQueryStringCollection()
{
byte[] queryStringBytes = this.QueryStringBytes;
if (queryStringBytes != null)
{
if (queryStringBytes.Length != 0)
{
this._queryString.FillFromEncodedBytes(queryStringBytes, this.QueryStringEncoding);
}
}
else if (!string.IsNullOrEmpty(this.QueryStringText))
{
this._queryString.FillFromString(this.QueryStringText, true, this.QueryStringEncoding);
}
}
先讓我們插入一點(diǎn) 那就是QueryString默認(rèn)已經(jīng)做了url解碼。 其中HttpValueCollection的 FillFromEncodedBytes方法如下
復(fù)制代碼 代碼如下:
internal void FillFromEncodedBytes(byte[] bytes, Encoding encoding)
{
int num = (bytes != null) ? bytes.Length : 0;
for (int i = 0; i < num; i++)
{
string str;
string str2;
this.ThrowIfMaxHttpCollectionKeysExceeded();
int offset = i;
int num4 = -1;
while (i < num)
{
byte num5 = bytes[i];
if (num5 == 0x3d)
{
if (num4 < 0)
{
num4 = i;
}
}
else if (num5 == 0x26)
{
break;
}
i++;
}
if (num4 >= 0)
{
str = HttpUtility.UrlDecode(bytes, offset, num4 - offset, encoding);
str2 = HttpUtility.UrlDecode(bytes, num4 + 1, (i - num4) - 1, encoding);
}
else
{
str = null;
str2 = HttpUtility.UrlDecode(bytes, offset, i - offset, encoding);
}
base.Add(str, str2);
if ((i == (num - 1)) && (bytes[i] == 0x26))
{
base.Add(null, string.Empty);
}
}
}
從這里我們可以看到QueryString已經(jīng)為我們做了解碼工作,我們不需要寫成 HttpUtility.HtmlDecode(Request.QueryString["xxx"])而是直接寫成Request.QueryString["xxx"]就ok了。
現(xiàn)在讓我們來(lái)看看你QueryString的驗(yàn)證,在代碼中有
復(fù)制代碼 代碼如下:
if (this._flags[1])
{
this._flags.Clear(1);
this.ValidateNameValueCollection(this._queryString, RequestValidationSource.QueryString);
}
一看this.ValidateNameValueCollection這個(gè)方法名稱就知道是干什么的了,驗(yàn)證QueryString數(shù)據(jù);那么在什么情況下驗(yàn)證的了?
讓我們看看this._flags[1]在什么地方設(shè)置的:
復(fù)制代碼 代碼如下:
public void ValidateInput()
{
if (!this._flags[0x8000])
{
this._flags.Set(0x8000);
this._flags.Set(1);
this._flags.Set(2);
this._flags.Set(4);
this._flags.Set(0x40);
this._flags.Set(0x80);
this._flags.Set(0x100);
this._flags.Set(0x200);
this._flags.Set(8);
}
}
而該方法在ValidateInputIfRequiredByConfig中調(diào)用,調(diào)用代碼
復(fù)制代碼 代碼如下:
internal void ValidateInputIfRequiredByConfig()
{
.........
if (httpRuntime.RequestValidationMode >= VersionUtil.Framework40)
{
this.ValidateInput();
}
}
我想現(xiàn)在大家都應(yīng)該明白為什么錯(cuò)題提示讓我們把framework改為2.0了吧。應(yīng)為在4.0后才驗(yàn)證。這種解決問(wèn)題的方法是關(guān)閉驗(yàn)證,那么我們是否可以改變默認(rèn)的驗(yàn)證規(guī)則了?
讓我們看看ValidateNameValueCollection
復(fù)制代碼 代碼如下:
private void ValidateNameValueCollection(NameValueCollection nvc, RequestValidationSource requestCollection)
{
int count = nvc.Count;
for (int i = 0; i < count; i++)
{
string key = nvc.GetKey(i);
if ((key == null) || !key.StartsWith("__", StringComparison.Ordinal))
{
string str2 = nvc.Get(i);
if (!string.IsNullOrEmpty(str2))
{
this.ValidateString(str2, key, requestCollection);
}
}
}
}
private void ValidateString(string value, string collectionKey, RequestValidationSource requestCollection)
{
int num;
value = RemoveNullCharacters(value);
if (!RequestValidator.Current.IsValidRequestString(this.Context, value, requestCollection, collectionKey, out num))
{
string str = collectionKey + "=\"";
int startIndex = num - 10;
if (startIndex <= 0)
{
startIndex = 0;
}
else
{
str = str + "...";
}
int length = num + 20;
if (length >= value.Length)
{
length = value.Length;
str = str + value.Substring(startIndex, length - startIndex) + "\"";
}
else
{
str = str + value.Substring(startIndex, length - startIndex) + "...\"";
}
string requestValidationSourceName = GetRequestValidationSourceName(requestCollection);
throw new HttpRequestValidationException(SR.GetString("Dangerous_input_detected", new object[] { requestValidationSourceName, str }));
}
}
哦?原來(lái)一切都明白了,驗(yàn)證是在RequestValidator做的。
復(fù)制代碼 代碼如下:
public class RequestValidator
{
// Fields
private static RequestValidator _customValidator;
private static readonly Lazy<RequestValidator> _customValidatorResolver = new Lazy<RequestValidator>(new Func<RequestValidator>(RequestValidator.GetCustomValidatorFromConfig));
// Methods
private static RequestValidator GetCustomValidatorFromConfig()
{
HttpRuntimeSection httpRuntime = RuntimeConfig.GetAppConfig().HttpRuntime;
Type userBaseType = ConfigUtil.GetType(httpRuntime.RequestValidationType, "requestValidationType", httpRuntime);
ConfigUtil.CheckBaseType(typeof(RequestValidator), userBaseType, "requestValidationType", httpRuntime);
return (RequestValidator) HttpRuntime.CreatePublicInstance(userBaseType);
}
internal static void InitializeOnFirstRequest()
{
RequestValidator local1 = _customValidatorResolver.Value;
}
private static bool IsAtoZ(char c)
{
return (((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')));
}
protected internal virtual bool IsValidRequestString(HttpContext context, string value, RequestValidationSource requestValidationSource, string collectionKey, out int validationFailureIndex)
{
if (requestValidationSource == RequestValidationSource.Headers)
{
validationFailureIndex = 0;
return true;
}
return !CrossSiteScriptingValidation.IsDangerousString(value, out validationFailureIndex);
}
// Properties
public static RequestValidator Current
{
get
{
if (_customValidator == null)
{
_customValidator = _customValidatorResolver.Value;
}
return _customValidator;
}
set
{
if (value == null)
{
throw new ArgumentNullException("value");
}
_customValidator = value;
}
}
}
主要的驗(yàn)證方法還是在CrossSiteScriptingValidation.IsDangerousString(value, out validationFailureIndex);而CrossSiteScriptingValidation是一個(gè)內(nèi)部類,無(wú)法修改。
讓我們看看CrossSiteScriptingValidation類大代碼把
復(fù)制代碼 代碼如下:
internal static class CrossSiteScriptingValidation
{
// Fields
private static char[] startingChars = new char[] { '<', '&' };
// Methods
private static bool IsAtoZ(char c)
{
return (((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')));
}
internal static bool IsDangerousString(string s, out int matchIndex)
{
matchIndex = 0;
int startIndex = 0;
while (true)
{
int num2 = s.IndexOfAny(startingChars, startIndex);
if (num2 < 0)
{
return false;
}
if (num2 == (s.Length - 1))
{
return false;
}
matchIndex = num2;
char ch = s[num2];
if (ch != '&')
{
if ((ch == '<') && ((IsAtoZ(s[num2 + 1]) || (s[num2 + 1] == '!')) || ((s[num2 + 1] == '/') || (s[num2 + 1] == '?'))))
{
return true;
}
}
else if (s[num2 + 1] == '#')
{
return true;
}
startIndex = num2 + 1;
}
}
internal static bool IsDangerousUrl(string s)
{
if (string.IsNullOrEmpty(s))
{
return false;
}
s = s.Trim();
int length = s.Length;
if (((((length > 4) && ((s[0] == 'h') || (s[0] == 'H'))) && ((s[1] == 't') || (s[1] == 'T'))) && (((s[2] == 't') || (s[2] == 'T')) && ((s[3] == 'p') || (s[3] == 'P')))) && ((s[4] == ':') || (((length > 5) && ((s[4] == 's') || (s[4] == 'S'))) && (s[5] == ':'))))
{
return false;
}
if (s.IndexOf(':') == -1)
{
return false;
}
return true;
}
internal static bool IsValidJavascriptId(string id)
{
if (!string.IsNullOrEmpty(id))
{
return CodeGenerator.IsValidLanguageIndependentIdentifier(id);
}
return true;
}
}
結(jié)果我們發(fā)現(xiàn)&# <! </ <? <[a-zA-z] 這些情況驗(yàn)證都是通不過(guò)的。
所以我們只需要重寫RequestValidator就可以了。
例如我們現(xiàn)在需要處理我們現(xiàn)在需要過(guò)濾QueryString中k=&...的情況
復(fù)制代碼 代碼如下:
public class CustRequestValidator : RequestValidator
{
protected override bool IsValidRequestString(HttpContext context, string value, RequestValidationSource requestValidationSource, string collectionKey, out int validationFailureIndex)
{
validationFailureIndex = 0;
//我們現(xiàn)在需要過(guò)濾QueryString中k=&...的情況
if (requestValidationSource == RequestValidationSource.QueryString&&collectionKey.Equals("k")&& value.StartsWith("&"))
{
return true;
}
return base.IsValidRequestString(context, value, requestValidationSource, collectionKey, out validationFailureIndex);
}
}
<httpRuntime requestValidationType="MvcApp.CustRequestValidator"/>
個(gè)人在這里只是提供一個(gè)思想,歡迎大家拍磚!
您可能感興趣的文章:
- Request.UrlReferrer中文亂碼解決方法
- 如何用ajax來(lái)創(chuàng)建一個(gè)XMLHttpRequest對(duì)象
- c# HttpWebRequest通過(guò)代理服務(wù)器抓取網(wǎng)頁(yè)內(nèi)容應(yīng)用介紹
- Javascript Request獲取請(qǐng)求參數(shù)如何實(shí)現(xiàn)
- Ajax通訊原理XMLHttpRequest
- jquery ajax學(xué)習(xí)筆記2 使用XMLHttpRequest對(duì)象的responseXML
- JavaScript下通過(guò)的XMLHttpRequest發(fā)送請(qǐng)求的代碼
- javascript一個(gè)無(wú)懈可擊的實(shí)例化XMLHttpRequest的方法
- javascript對(duì)XMLHttpRequest異步請(qǐng)求的面向?qū)ο蠓庋b
- jQuery ajax(復(fù)習(xí))—Baidu ajax request分離版
相關(guān)文章
asp.net DZ論壇中根據(jù)IP地址取得所在地的代碼
從dz .net版發(fā)現(xiàn)的這個(gè)不錯(cuò)的函數(shù),大家以后就可以方便調(diào)用了2008-10-10使用ASP.NET中關(guān)于代碼分離的實(shí)例分享
本文主要簡(jiǎn)單介紹了如何讓代碼分離閱讀起來(lái)更方便,不至于代碼過(guò)于臃腫,這里舉一反三,希望對(duì)大家有所幫助。2016-04-04ASP.NET?MVC開(kāi)發(fā)接入微信公共平臺(tái)
這篇文章主要為大家介紹了微信平臺(tái)開(kāi)發(fā)ASP.NET?MVC接入微信公共平臺(tái)實(shí)現(xiàn)過(guò)程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-04-04ASP.NET Core Authentication認(rèn)證實(shí)現(xiàn)方法
這篇文章主要介紹了ASP.NET Core Authentication認(rèn)證實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08.NET之生成數(shù)據(jù)庫(kù)全流程實(shí)現(xiàn)
這篇文章主要介紹了.NET之生成數(shù)據(jù)庫(kù)全流程實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-05-05通過(guò)C#動(dòng)態(tài)生成圖書信息XML文件
通過(guò)C#動(dòng)態(tài)生成圖書信息XML文件,下面有個(gè)不錯(cuò)的示例,需要的朋友可以參考下2013-11-11VS2015 免費(fèi)插件Refactoring Essentials
Refactoring Essentials是一款用于代碼分析和重構(gòu)的開(kāi)源免費(fèi)VS2015插件,其功能豐富強(qiáng)大,必然會(huì)成為類似Web Essentials這樣的必備插件。2015-07-07