`
piperzero
  • 浏览: 3477863 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
文章分类
社区版块
存档分类
最新评论

使用 Request.QueryString 接受参数时,跟编码有关的一些问题

阅读更多

我们先来看以下几个请求,看a.aspx 页面用Request.QueryString接受到的是啥信息?

页面URL Request.QueryString["info"]接受到的值
案例一 a.aspx?info=%25

%

案例二 a.aspx?info=%bc%bc%ca%f5

����

情况分析:

案例一

a.aspx?info=%25 为何 Request.QueryString["info"]接受到的值是 % ,而不是 %25,是因为Request.QueryString 替我们在接受到值后,做了一次URL解码。 HttpUtility.UrlDecode("%25") 的计算结果就是 %

上面的这个案例一虽然看起来很简单。但是我们在一些特殊场景时候,就会因为这个而极度郁闷。

比如以下几种情况:

你有一个自己的加密算法,而这个加密算法,某些情况下会计算出带百分号的结果,而这个结果你是要通过URL参数的方式传递给其它页面的。
这时候你就苦恼的发现,某些时候某个功能就不能用。

如果解决案例一碰到的情况呢?

解决方案一:

把需要传递的参数传递前作一次 HttpUtility.UrlEncode ,
记得是按照 UTF-8 的编码的 UrlEncode 。这样如果我们希望客户端接受到的是 %25 就应该传递的是 %2525 。

切记,不可在接受方每次接受后,自作聪明的都做一次 UrlEncode 。而是在发送方做 UrlEncode 。
如果接受方接受后作 UrlEncode 的话,就会出现下面情况:
发送方发送 a.aspx?info=%25 ,这时候如果接受方 接受后作 UrlEncode 的话,一切正确
发送方发送 a.aspx?info=% ,这时候如果接受方 接受后作 UrlEncode 的话,则就乱了。

另:这套方案中切记, UrlEncode 和 UrlDecode 的次数应该一一对应。不能多一次,也不能少一次。
有人就会说,这还会出现次数不对应么? 比如下面情况,一个不留意就很可能出现次数不对应。而出现不是你所期望的情况。
比如我们有这样类似的功能:

a.aspx 页面中,根据传入的 from 参数,自动跳转到 from 参数(用Request.QueryString["from"]来接受这个参数)设置的页面。
b.aspx 页面也是同样的逻辑,根据传入的 from 参数(用Request.QueryString["from"]来接受这个参数),自动跳转到指定的页面。
c.aspx 页面也是同样的逻辑,根据传入的 from 参数(用Request.QueryString["from"]来接受这个参数),自动跳转到指定的页面。


这样我们就可能书写下面的链接地址:
a.aspx?from=b.aspx
a.aspx?from=b.aspx?from=c.aspx
a.aspx?from=b.aspx?from=c.aspx?from=http://blog.joycode.com/ghj/

下面再复杂一点,我给下面几个链接,其中都有 a 这个参数,请告诉我 a 这个参数是被那个页面接受到了?
说明: HttpUtility.UrlEncode("&") == "%26" HttpUtility.UrlEncode("%") == "%25"

地址 a 参数会被那个页面接受到
a.aspx?from=b.aspx?from=c.aspx&a=1 a 参数被 a.aspx 页面接受到了
a.aspx?from=b.aspx?from=c.aspx%26a=1 a 参数被 b.aspx 页面接受到了
a.aspx?from=b.aspx?from=c.aspx%2526a=1 a 参数被 c.aspx 页面接受到了

如果想不明白,就想想下面这句话
每一次用 Request.QueryString 获取参数时候,就作了一次 HttpUtility.UrlDecode。

解决方案二:

不用 Request.QueryString ,而是自己实现一个获取查询参数的方法。细节我在案例二讲完后再告诉大家,因为这个解决方案也处理了案例二的一些情况。

案例二

a.aspx?info=%bc%bc%ca%f5 传给我们的信息其实是使用 GB2312 编码后的“技术” 这两个汉字。
不信,你可以用下面表达式计算的结果就是 %bc%bc%ca%f5
HttpUtility.UrlEncode("技术", System.Text.Encoding.GetEncoding("GB2312"))

ASP.net 系统内部,在处理 Request.QueryString 等情况时候,都是使用的 UTF-8 的编码,我们如果不存在多系统并存的问题时候,这个问题一点都不存在。
但是,当需要跟其它系统交互式后,问题就可能会出现。
如果你不了解案例二这里情况时,你就会被这个问题苦恼死。

比如下面这两个地址提到的问题:

ASP.net中的Server.UrlEncode函数和ASP中的Server.URLEncode函数返回的值竟然不一样
http://blog.joycode.com/ghj/archive/2003/10/20/2992.aspx

PHP与aspx之间中文通过URL如何传递?
http://topic.csdn.net/u/20071018/19/8a4066af-a08c-4214-91e9-ed4caf977e07.html

案例二的解决方案
使用带编码的 HttpUtility.ParseQueryString 函数

就是采用类似下面代码的方式,来获得指定格式编码的查询文本参数。

System.Collections.Specialized.NameValueCollection nv =
System.Web.HttpUtility.ParseQueryString(Request.Url.Query, System.Text.Encoding.GetEncoding("GB2312"));
Response.Write(nv["Tag"]);

要说我为啥知道上面几种解决方案,是因为我用 Reflector 看了 Request.QueryString 的实现代码。在查看代码时候,我们会看到这样一个 internal 方法:
System.Web.HttpValueCollection 类的内部方法:
internal void FillFromString(string s, bool urlencoded, Encoding encoding)

这个内部方法实现了,按需解密查询参数的功能,但是遗憾的是,在QueryString 的处理函数中,强制指定了解析 QueryString 时,必须作一次 HttpUtility.UrlDecode。参看如下代码:

public static NameValueCollection ParseQueryString(string query, Encoding encoding)
{
...
return new HttpValueCollection(query, false, true, encoding);
}

如果我们不想采用案例一的解决方案一,我们就需要自己写一个解析查询信息的代码。我们完全可以照抄 System.Web.HttpValueCollection 类的 internal void FillFromString(string s, bool urlencoded, Encoding encoding) 方法来改写。但郁闷的是:如果你用 Reflector 察看这个函数的实现时候,Reflector 出来的代码是错误的。正确的方法如下:是在施凡帮助下完成的。

自己实现从 URL 查询文本 Query 中解析出我们自己需要的文本的方法

/// <summary></summary>
/// 根据 URL 中的 查询文本 Query 解析成一个 NameValueCollection
/// 在装配脑袋帮助下 郭红俊 改编自 System.Web.HttpValueCollection 类的内部方法:
/// internal void FillFromString(string s, bool urlencoded, Encoding encoding)
///
/// 需要解析的查询文本
/// 解析文本时候是否需要URL解码
/// 解析文本时候,按照那种URL编码进行解码
/// <returns></returns>
public static NameValueCollection FillFromString(string query, bool urlencoded, Encoding encoding)
{
NameValueCollection queryString = new NameValueCollection();
if (string.IsNullOrEmpty(query))
{
return queryString;
}

// 确保 查询文本首字符不是 ?
if (query.StartsWith("?"))
{
query = query.Substring(1, query.Length - 1);
}

int num1 = (query != null) ? query.Length : 0;
// 遍历每个字符
for (int num2 = 0; num2 < num1; num2++)
{
int num3 = num2;
int num4 = -1;
while (num2 < num1)
{
switch (query[num2])
{
case '=':
if (num4 < 0)
{
num4 = num2;
}
break;
case '&':
goto BREAKWHILE;
}
num2++;
}

BREAKWHILE:

string name = null;
string val = null;
if (num4 >= 0)
{
name = query.Substring(num3, num4 - num3);
val = query.Substring(num4 + 1, (num2 - num4) - 1);
}
else
{
val = query.Substring(num3, num2 - num3);
}
if (urlencoded)
{

queryString.Add(HttpUtility.UrlDecode(name, encoding), HttpUtility.UrlDecode(val, encoding));
}
else
{
queryString.Add(name, val);
}
if ((num2 == (num1 - 1)) && (query[num2] == '&'))
{
queryString.Add(null, string.Empty);
}
}

return queryString;

}

用上面的代码,我们就可以按需解析自己需要的查询参数,而不是受限的使用Request.QueryString 。

小结

Request.QueryString 替我们件事情:每次接受到参数后,都做 UrlEncode ,并且是按照 UTF-8编码做的 UrlEncode 。 这在大多数情况下没有任何问题,但是一些情况下,会给我们带来麻烦,本文就是分析这些可能给我们带来麻烦的场景,以及解决方法。

参考资料:

使用 Reflector ; 查看代码时候,碰到的一个Reflector 的bug
http://blog.joycode.com/ghj/archive/2006/12/06/88646.aspx

解密不同编码的的参数。
http://blog.joycode.com/ghj/archive/2006/04/19/74894.aspx

分享到:
评论

相关推荐

    asp页面和Asp.net页面传中文参数UrlEncode编码以及接收解码

    str=”+HttpUtility.UrlEncode(str) ,解码方式为HttpUtility.UrlDecode(Request.QueryString[“str”].ToString().Trim()) asp的Get方式传送为”webPage.aspx?str=”+server.urlencode(str) 两种编码不统一 解决方案...

    EL表达式 (详解)

    ${pageContext.request.queryString} 取得请求的参数字符串 ${pageContext.request.requestURL} 取得请求的URL,但不包括请求之参数字符串 ${pageContext.request.contextPath} 服务的web application 的...

    ajax请求get与post的区别总结

    get如果请求url没有变化,取出缓存,提高效率;...get方式,服务器端用Request.QueryString获取变量的值; post方式,服务器端用Request.Form获取提交的数据; 两种方式的参数都可以用Request来获得。

    PageNavigater 2.0(含ASP.NET DEMO)

    protected void Page_Load(object sender, EventArgs e) { string cp = Request.QueryString[_pageVariable]; if (null != cp) _currentPage = Convert.ToInt32(cp); else _current...

    connect-base64:中间件,用于根据base64编码的查询参数设置请求主体

    连接base64 连接中间件以解码来自querystring参数的base64请求并将其添加到请求正文中用法connect-base64([key]) 默认情况下,使用字符串key或使用data参数。 从查询字符串中读取密钥,然后将其用作请求正文。 var...

    springmybatis

    其实还有更简单的方法,而且是更好的方法,使用合理描述参数和SQL语句返回值的接口(比如IUserOperation.class),这样现在就可以至此那个更简单,更安全的代码,没有容易发生的字符串文字和转换的错误.下面是详细...

    java 面试题 总结

    redirect就是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址,一般来说浏览器会用刚才请求的所有参数重新请求,所以session,request参数都可以获取。 20、EJB与JAVA BEAN的区别? Java Bean 是可复用...

    超级有影响力霸气的Java面试题大全文档

    redirect就是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址,一般来说浏览器会用刚才请求的所有参数重新请求,所以session,request参数都可以获取。 23、EJB与JAVA BEAN的区别?  Java Bean 是可...

    JAVA程序员面试题

    这既包括 servlet 又包括被编译成 servlet 的 JSP 页面 request是是代表与 Web 客户机发出的一个请求相关的对象和属性。一个请求可能跨越多个页面,涉及多个 Web 组件(由于 forward 指令和 include 动作的关系) ...

    java-servlet-api.doc

    当Servlet引擎决定卸载一个Servlet时(例如,如果这个引擎被关闭或者需要让资源),这个引擎必须允许Servlet释放正在使用的资源并存储有关资料。为了完成以上工作,引擎会调用Servlet的destroy()方法。 在卸载一个...

    C#编程经验技巧宝典

    14 &lt;br&gt;0028 “///”符号的使用技巧 14 &lt;br&gt;0029 使用注释取消程序语句的执行 15 &lt;br&gt;2.2 语句 15 &lt;br&gt;0030 跳转语句GOTO的使用 15 &lt;br&gt;0031 Continue语句的使用 16 &lt;br&gt;0032 Break...

Global site tag (gtag.js) - Google Analytics