Request 接收参数乱码原理解析

简介:

起因:

今天早上被同事问了一个问题:说接收到的参数是乱码,让我帮着解决一下。

 

实际情景:

复制代码
同事负责的平台是Ext.js框架搭建的,web.config配置文件里配置了全局为“GB2312”编码:

< globalization requestEncoding = " gb2312 "  responseEncoding = " gb2312 "  fileEncoding = " gb2312 "  culture = " zh-CN " />

当前台提交“中文文字”时,后台用Request.QueryString[
" xxx " ]接收到的是乱码。

无论用System.Web.HttpUtility.UrlDecode(
" xxx " , " 编码类型 " )怎么解码都无效。
复制代码

 

原理说明:

1:首先确定的是:客户端的url参数在提交时,Ext.js会对其编码再提交,而客户端的编码默认是utf-8编码

客户端默认有三种编码函数:escape() encodeURI() encodeURIComponent()

 

2:那为什么用Request.QueryString["xxx"]接收参数时,收到的会是乱码?

为此,我们必须解开Request.QueryString的原始处理逻辑过程

 

我们步步反编绎,

2.1:看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 );
            ValidateNameValueCollection(
this ._queryString,  " Request.QueryString " );
        }
        
return   this ._queryString;
    }
}
复制代码

 

2.2:切入 FillInQueryStringCollection()方法

复制代码
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))
    {
        
// 下面这句是对普通文件提交的处理:FillFromString是个切入点,编码切入点是:this.QueryStringEncoding
         this ._queryString.FillFromString( this .QueryStringText,  true this .QueryStringEncoding);
        
    }
}

复制代码

 

2.3:切入:QueryStringEncoding

复制代码
internal  Encoding QueryStringEncoding
{
    
get
    {
        Encoding contentEncoding 
=   this .ContentEncoding;
        
if  ( ! contentEncoding.Equals(Encoding.Unicode))
        {
            
return  contentEncoding;
        }
        
return  Encoding.UTF8;
    }
}
// 点击进入this.ContentEncoding则为:
public  Encoding ContentEncoding
{
    
get
    {
        
if  ( ! this ._flags[ 0x20 ||  ( this ._encoding  ==   null ))
        {
            
this ._encoding  =   this .GetEncodingFromHeaders();
            
if  ( this ._encoding  ==   null )
            {
                GlobalizationSection globalization 
=  RuntimeConfig.GetLKGConfig( this ._context).Globalization;
                
this ._encoding  =  globalization.RequestEncoding;
            }
            
this ._flags.Set( 0x20 );
        }
        
return   this ._encoding;
    }
    
set
    {
        
this ._encoding  =  value;
        
this ._flags.Set( 0x20 );
    }
}
复制代码

说明:

从QueryStringEncoding代码得出,系统默认会先取globalization配置节点的编码方式,如果取不到,则默认为UTF - 8编码方式

 

2.4:切入  FillFromString(string s, bool urlencoded, Encoding encoding)

复制代码
internal   void  FillFromString( string  s,  bool  urlencoded, Encoding encoding)
{
    
int  num  =  (s  !=   null ?  s.Length :  0 ;
    
for  ( int  i  =   0 ; i  <  num; i ++ )
    {
        
int  startIndex  =  i;
        
int  num4  =   - 1 ;
        
while  (i  <  num)
        {
            
char  ch  =  s[i];
            
if  (ch  ==   ' = ' )
            {
                
if  (num4  <   0 )
                {
                    num4 
=  i;
                }
            }
            
else   if  (ch  ==   ' & ' )
            {
                
break ;
            }
            i
++ ;
        }
        
string  str  =   null ;
        
string  str2  =   null ;
        
if  (num4  >=   0 )
        {
            str 
=  s.Substring(startIndex, num4  -  startIndex);
            str2 
=  s.Substring(num4  +   1 , (i  -  num4)  -   1 );
        }
        
else
        {
            str2 
=  s.Substring(startIndex, i  -  startIndex);
        }
        
if  (urlencoded) // 外面的传值默认是true,所以会执行以下语句
        {
            
base .Add(HttpUtility.UrlDecode(str, encoding), HttpUtility.UrlDecode(str2, encoding));
        }
        
else
        {
            
base .Add(str, str2);
        }
        
if  ((i  ==  (num  -   1 ))  &&  (s[i]  ==   ' & ' ))
        {
            
base .Add( null string .Empty);
        }
    }
}

复制代码

说明:

从这点我们发现:所有的参数输入,都调用了一次:HttpUtility.UrlDecode(str2, encoding);

 

3:结论出来了

当客户端js对中文以utf - 8编码提交到服务端时,用Request.QueryString接收时,会先以globalization配置的gb2312去解码一次,于是,产生了乱码。

所有的起因为:

1 :js编码方式为urt - 8

2 :服务端又配置了默认为gb2312

3 :Request.QueryString默认又会调用HttpUtility.UrlDecode用系统配置编码去解码接收参数。

 

文章补充

复制代码
1 :系统取默认编码的顺序为:http请求头 -> globalization配置节点 - 》默认UTF - 8

2 :在Url直接输入中文时,不同浏览器处理方式可能不同如:ie不进行编码直接提交,firefox对url进行gb2312编码后提交。

3 :对于未编码“中文字符”,使用Request.QueryString时内部调用HttpUtility.UrlDecode后,由gb2312 -> utf - 8时,

如果查不到该中文字符,默认转成
" %ufffd " ,因此出现不可逆乱码。

复制代码

 

 

4:解决之路

知道了原理,解决的方式也有多种多样了:

1:全局统一为UTF-8编码,省事又省心。

 

2:全局指定了GB2312编码时,url带中文,js非编码不可,如ext.js框架。

这种方式你只能特殊处理,在服务端指定编码解码,
因为默认系统调用了一次HttpUtility.UrlDecode("xxx",系统配置的编码),
因此你再调用一次HttpUtility.UrlEncode("xxx",系统配置的编码),返回到原始urt
- 8编码参数
再用HttpUtility.UrlDecode("xxx",utf-8),
解码即可。

 

5:其它说明:默认对进行一次解码的还包括URI属性,而Request.RawUrl则为原始参数

 

 


相关文章
|
存储 监控 安全
电脑格式化了还能恢复数据吗?
在日常使用电脑的过程中,我们可能会因为各种原因需要格式化硬盘。然而,格式化操作会清除硬盘上的所有数据,很多人担心格式化后数据无法找回。本文将详细介绍电脑格式化后的数据恢复方法,帮助大家在不小心格式化硬盘后,仍有机会找回重要文件。
电脑格式化了还能恢复数据吗?
|
11月前
|
机器学习/深度学习 人工智能 自然语言处理
MetaGPT开源自动生成智能体工作流,4.55%成本超GPT-4o
AFlow是由Jiayi Zhang等学者提出的一项新研究,发表于arXiv。它通过将工作流优化问题转化为代码表示空间中的搜索,并引入蒙特卡洛树搜索(MCTS)算法,实现了高效的工作流自动化生成与优化。在六个基准数据集上,AFlow性能比现有基线平均提高5.7%,并使小模型以较低成本超越GPT-4。尽管存在一些局限性,如通用性和计算复杂度,AFlow为降低大型语言模型应用成本提供了新思路,推动了人工智能技术的进步。论文地址:https://arxiv.org/abs/2410.10762。
352 27
|
存储 SQL 分布式计算
Hadoop生态系统概述:构建大数据处理与分析的基石
【8月更文挑战第25天】Hadoop生态系统为大数据处理和分析提供了强大的基础设施和工具集。通过不断扩展和优化其组件和功能,Hadoop将继续在大数据时代发挥重要作用。
|
人工智能 前端开发 数据可视化
手猫助手Agent技术探索总结(2)
手猫助手Agent技术探索总结
331 8
|
域名解析 监控 网络协议
Ping命令的替代工具有哪些?
【8月更文挑战第14天】Ping命令的替代工具有哪些?
1004 2
|
存储 人工智能 分布式计算
阿里云智能大数据演进
本文根据7月24日飞天发布时刻产品发布会、7月5日DataFunCon2024·北京站:大数据·大模型.双核时代实录整理而成
|
Kubernetes jenkins Shell
|
Rust Shell 开发者
7步开始rust(自定义环境安装与vscode开发环境配置)
本文档详细记录了当改变Rust安装路径至特定目录(如 `/home/local_rust`)时,如何在VSCode环境中正确配置Rust开发环境的过程。主要包括:设置环境变量、使用清华大学镜像安装Rust及更新镜像源、手动部署 `rust-analyzer`、安装标准库源码、安装与配置VSCode插件等七个步骤,确保开发者能够顺利搭建并使用定制化的Rust开发环境。
1997 0
|
存储 缓存 Linux
音频播放的一些整理
音频播放的一些整理
365 1
|
安全 网络安全 数据安全/隐私保护
此网站无法提供安全连接(客户端和服务器不支持一般 SSL 协议版本或加密套件。)
此网站无法提供安全连接(客户端和服务器不支持一般 SSL 协议版本或加密套件。)
1570 0