#circle { background-color: #8fcbec; border: 3px }

概述

  开发Web项目的过程中,经常遇到浏览器中显示的内容乱码,或者服务器获取浏览器请求参数时乱码的问题,很多同学基本都是在遇到乱码的时候去网上一顿搜索,然后看哪篇文章比较靠谱就照着上面的内容去配后乱码成功消失了,然后就没然后了...

最后基本只是停留在知道怎么样设置能避免常见的乱码问题,而不知道具体的原理,一旦遇到了网上查不到的乱码场景就不知道如何解决了~

  本文会深入的让你了解针对于HTTP请求时,这一去一回(Request,Response)之间,到底做了怎样的事情,让你彻底告别Web项目中的乱码烦恼。本文的内容是基于Tomcat 8.0.23版本的,其他容器也可以参考本文的内容,毕竟理论都是通的~

Request乱码

  在Request过程中我们需要注意2个步骤,第一个是请求发送时所使用的编码,第二个是应用收到请求后解码时所用的编码,只有保证这两步中使用相同的编码即可有效防止乱码的发生。那么请求时使用的是什么编码呢?这个主要取决于请求时的客户端。

下面我们做个测试,客户端分别使用浏览器和curl来请求,服务端使用Tomcat 8.0.23来处理。

  请求地址:http://localhost:8080/ccj/楚楚街?query=买的漂亮  

  我们先来分析下上面的地址:

  url:http://localhost:8080/楚楚街

  uri:/ccj/楚楚街

  queryString:query=买的漂亮

Tomcat使用默认配置,使用下面代码来接收请求:

Firefox中请求 http://localhost:8080/ccj/楚楚街?query=买的漂亮 结果如下:

上面信息可以看出,在发送请求之前Firefox先对请求地址中的中文使用UTF-8编码进行了百分号编码,关于百分号编码的内容可以自行Google查阅相关内容,这里不在赘述。

这里我们发现获取的内容居然没有乱码,这是因为我是用的是Tomcat8的缘故,看下官方说明:

在不指定URIEncoding的情况下,并且也没指定系统属性org.apache.catalina.STRICT_SERVLET_COMPLIANCE=true时,默认使用UTF-8进行解码。这个变化是从Tomcat8开始的,Tomcat8之前的的处理方式是,如果没指定

URIEncoding的情况下,那么会直接使用ISO-8859-1作为默认编码的。

下面我们加上系统属性org.apache.catalina.STRICT_SERVLET_COMPLIANCE=true来模拟Tomcat8之前的情况,发起同样的请求 http://localhost:8080/ccj/楚楚街?query=买的漂亮 结果如下:

看结果发现,果然出现了乱码问题,那么我们已经知道了Firefox发送过来的请求是经过了UTF-8编码的百分号编码,那么我们可以对query进行ISO-8859-1编码的百分号解码来重现下乱码,看结果:

  我靠~什么鬼为什么乱码跟我们预想的结果不一样,这个乱码跟之前访问Sevlet中出现的乱码不一样???难道之前的结论都是错的么???其实并不是~这里出现2次乱码显示的不一致的情况是控制台编码不一致导致的,

我是用的IDEA,IDEA的控制台编码是受到-Dfile.encoding这个虚拟机启动参数影响的,这里送上之前写的一篇文章供参考(Java虚拟机(JVM)默认字符集详解)。

================================================华丽的分割线之IDEA乱码问题开始================================================

  既然遇到了控制台编码乱码问题,接下来我就顺便讲一下IDEA控制台乱码的解决方法吧~首先我们看上面2附图,先看第一个图请求Servlet时控制台打印的是 file.encoding=GBK,而第二个图中使用main方法运行后

控制台打印的是 file.encoding=UTF-8。2次运行使用的都是同一个IDEA实例,为什么编码会不一样呢?我们先关掉IDEA,然后杀死所有当前java进程,然后重新启动IDEA,看一下进程启动参数,因为是windows系统,所以cmd中使用命令:

wmic  process where caption="java.exe" get caption,commandline /value

Linux下使用命令:ps -aux | grep java

结果如下图:

看见了吧,-Dfile.encoding=GBK这个是当前IDEA实例启动时候的JVM虚拟参数,所以当前IDEA的控制台编码是GBK,但为什么我们跑main方法时打印的信息是-Dfile.encoding=UTF-8呢,这是因为我修改了Settings

中的File Encodings导致的,直接看图:

这个地方的配置会影响运行main方法时的控制台编码,那么既然知道了原因,我们就开始解决问题吧~首先我们需要更改IDEA启动时的参数-Dfile.encoding=UTF-8,修改的方式为:

windows x64环境修改 C:\Program Files (x86)\JetBrains\IntelliJ IDEA 2017.1\bin\idea64.exe.vmoptions

windows x86环境修改 C:\Program Files (x86)\JetBrains\IntelliJ IDEA 2017.1\bin\idea.exe.vmoptions

分不清楚的可以将上面2个文件全部修改~修改内容为在文件末尾加上红框中的内容:

MacOS系统修改文件 /Applications/IntelliJ IDEA 2017.app/Contents/Info.plist,修改完之后重启IDEA才能生效~

重启后我们再次访问 http://localhost:8080/ccj/楚楚街?query=买的漂亮 结果如下:

结果发现依然是乱码,并且乱码跟最开始访问的时候不一样了...这是什么鬼...我们继续来分析,因为是运行在Tomcat中,所以我们在打印的时候会根据启动Tomcat的那个JVM的file.encoding进行编码,

通过查看线程启动参数发现Tomcat启动时的file.encoding=GBK,那么问题就迎刃而解了,原理简述如下:

  1.System.out.println("query: " + query);时,首先根据启动Tomcat的那个JVM的file.encoding进行编码(也就是GBK),然后将字节流传给控制台。

  2.控制台收到字节流,然后根据启动IDEA的那个JVM的file.encoding进行解码(也就是UTF-8),然后显示在控制台。

由于上面的编码和解码的不同,导致了最终的乱码,针对上面的结论我们可以使用main方法来模拟下,如下图:

看见了吧,乱码跟之前的乱码相同了~到此原理已经懂了,那么就直接解决问题吧,给Tomcat的启动参数加上-Dfile.encoding=utf-8后,再次访问 http://localhost:8080/ccj/楚楚街?query=买的漂亮 结果如下:

这次OK了,乱码的内容跟我们在main方法里面模拟的内容一致了~至此,由于IDEA引起的乱码问题解决了,我们继续讨论Request的乱码问题~

================================================华丽的分割线之IDEA乱码问题结束================================================

根据上面的分析后,我们知道了Request乱码产生的原因,就是因为请求发送时的编码和接收请求时解码时的编码不一致导致的,那么我们需要做的就是保证他们都使用相同的编码即可。

总结:

  解决Request请求地址中中文乱码问题如下:

  1.Tomcat8以及之后,我们只需要使用Tomcat默认的配置即可在收到请求时默认使用UTF-8编码进行解码。

  2.Tomcat8之前,我们需要修改 C:\Program Files\apache-tomcat-8.0.23\conf\server.xml 文件,参考下图:

  

  在图中位置,添加红框中的内容即可~

到此为止,Request请求地址中中文乱码问题解决了~接下来我们来看看Request的body中的乱码问题。

我们直接将刚才的GET请求 http://localhost:8080/ccj/楚楚街?query=买的漂亮 转换成POST请求 http://localhost:8080/ccj/楚楚街 并将请求参数(query=买的漂亮)放入请求体中,如下图:

然后我们看下Servlet中打印的结果:

发现获取参数query的时候依然是乱码,此时我们只需要在所有Request的访问之前设置一下req.setCharacterEncoding("UTF-8");即可,设置重新访问结果如下:

乱码成功解决,至此浏览器发起Request请求乱码问题解决~

下面我们在看看通过工具发起Request的情况会是什么样~

我们这里使用Fiddler进行模拟GET请求:

然后我们看控制台结果:

结果发现,居然又乱码了...这次的原因是,因为我们使用的是工具而不是浏览器,浏览器是遵循HTTP规范的,而工具则不会,导致本次测试最后乱码的根本原因就是没有进行百分号编码。

这里简单说下原理:

  1.Fiddler在请求的时候对请求地址做了UTF-8编码,拿query=买的漂亮来举例,在发送Request之前会对URL进行编码(这里是UTF-8)为字节流后发送给服务端,因为Fiddler没有做百分号编码,所以在对URL进行编码发送的时候,

   URL中的query内容就是 买的漂亮 这四个汉字,买的漂亮 进行UTF-8编码后为e4b9b0(买)e79a84(的)e6bc82(漂)e4baae(亮)。

  2.服务端在收到Request后,会将URL地址的字节流存入ByteChunk中,而我们只有在获取url、uri、queryString的时候出现了乱码的情况,这是因为在获取这3个值的时候,是不会对字节流进行解码操作的,而ByteChunk中有个默认

   的Charset(即ISO-8859-1),在不进行解码的情况下(即没显式的调用ByteChunk中的setCharset(Charset charset)方法的时候),默认会使用这个默认值ISO-8859-1对获取的字节流直接解码,因此在解码

   e4b9b0(买)e79a84(的)e6bc82(漂)e4baae(亮)的时候就乱码了,因为ISO-8859-1无法表示汉字。

  3.所以对URL进行百分号编码是非常有必要的~

如果对上面的e4b9b0(买)e79a84(的)e6bc82(漂)e4baae(亮)进行百分号编码的话, 那么在到达URL字节流编码这一步之前,URL中的 买的漂亮 就会被替换为 "%E4%B9%B0%E7%9A%84%E6%BC%82%E4%BA%AE",

之后URL进行字节流编码时,是基于字符串"%E4%B9%B0%E7%9A%84%E6%BC%82%E4%BA%AE"进行的,因为里面都是ISO-8859-1表示范围内的字符,所以在服务端对百分号编码后的字节流进行直接获取的时候(也就是不对字节流

进行解码,而使用ByteChunk中默认的编码ISO-8859-1进行解码),就不会出现乱码了,其返回的内容就是百分号编码后的内容~

浏览器直接请求:http://localhost:8080/ccj/楚楚街?query=买的漂亮 结果如下:

至此,Request乱码问题全部解决~

总结:

  解决Request请求地址中中文乱码问题如下:

    1.Tomcat8以及之后,我们只需要使用Tomcat默认的配置即可在收到请求时默认使用UTF-8编码进行解码。

    2.Tomcat8之前,我们需要修改 C:\Program Files\apache-tomcat-8.0.23\conf\server.xml 文件,在Connector中添加URIEncoding="UTF-8"。

  解决Request请求体中的中文乱码问题如下:

    1.在所有Request的访问之前设置一下req.setCharacterEncoding("UTF-8");即可。

  解决使用工具请求导致对原始字节流进行获取时的乱码问题(如:req.getRequestURL()、req.getRequestURI()、req.getQueryString()等方法):

    1.对URL进行指定字符集的百分号编码即可。

写了大半天时间...希望能对遇到乱码的同学有所帮助~其实我也挺懒的...很久之前就想写这篇文章了一直拖到现在才写~o(∩_∩)o ~

下一篇内容会针对Resopnse的乱码问题展开讲解,这里附上链接方便跳转:一文让你从此告别HTTP乱码(二)Response篇

一文让你从此告别HTTP乱码(一)Request篇的更多相关文章

  1. 一文让你从此告别HTTP乱码(二)Response篇

    #circle { background-color: #8fcbec; border: 3px } 概述 开发Web项目的过程中,经常遇到浏览器中显示的内容乱码,或者服务器获取浏览器请求参数时乱码的 ...

  2. 上传Text文档并转换为PDF(解决乱码)

    前些日子,Insus.NET有分享一篇<上传Text文档并转换为PDF>http://www.cnblogs.com/insus/p/4313092.html 它是按最简单与默认方式来处理 ...

  3. 可遇不可求的Question之导入mysql中文乱码解决方法篇

    可遇不可求的Question之导入mysql中文乱码解决方法篇 先 set names utf8;然后 source c:\1.sql ?

  4. 手机打开PDF文档中文英文支持(乱码问题)解决攻略

    电子书的优点很多,随时随地阅读,无论白天黑夜走路坐车都能阅读:想确认一下某句话是不是这本书里的,搜索一下就可以知道:搬家也不用发愁,几万本书带在身上,依然轻松步行.我买了一台平板主要动因就是为了看书, ...

  5. 【热门技术】EventBus 3.0,让事件订阅更简单,从此告别组件消息传递烦恼~

    一.写在前面 还在为时间接收而烦恼吗?还在为各种组件间的消息传递烦恼吗?EventBus 3.0,专注于android的发布.订阅事件总线,让各组件间的消息传递更简单!完美替代Intent,Handl ...

  6. 异步调试神器Slog,“从此告别看日志,清日志文件了”

    微信调试.API调试和AJAX的调试的工具,能将日志通过WebSocket输出到Chrome浏览器的console中  — Edit 92 commits 4 branches 3 releases ...

  7. VisualSVN Server 从此告别SVN记事本配置

    http://www.visualsvn.com/downloads/ 注意下载的是Server版本,他还会提供一个visual Studio的插件:   安装完毕后,可以在管理界面进行角色添加,创建 ...

  8. 前端开发利器 livereload -- 从此告别浏览器F5键

    各位从事前端开发的童鞋们,大家每天coding && coding,然后F5 && F5,今天推荐一个静态文件在浏览器中自动更新的扩展 livereload,不同手动刷 ...

  9. 昼猫笔记 从此告别复杂代码--JavaScript

    昼猫笔记--给你带来不一样的笔记 不止是笔记 更多的是思考 大家好,我是一只来自喵星的前端初学者,由于我们喵星人科技较为落后,昼猫从今天开始带着使命来到地球学习前端知识. 从今天开始,猫猫我就从Jav ...

随机推荐

  1. java系列笔记---正则表达式(1)常用符号

    正则表达式---常用符号 首先声明,我这里列表的是经常使用的一些符号,如果你想得到全部,那建议你通过API中,搜索Pattern类,会得到所有符号. 字符类 [abc] a.b 或 c(简单类) [^ ...

  2. 如何使用Babel将ES6转码为ES5?

    一.前言: 当我们还在沉迷于ES5的时候,殊不知ES6早就已经发布几年了.时代在进步,WEB前端技术也在日新月异,是时候做些改变了! ECMAScript 6(ES6)的发展速度非常之快,但现代浏览器 ...

  3. Orcale 之基本术语二

    表空间 表空间是 Orcale 数据库最大的逻辑结构.表空间就是一个或者多个数据文件的集合.所有的数据文件都被逻辑的存放在表空间中. 一个数据库包括 SYSTEM.SYSAUX和TMP三个默认表空间, ...

  4. GCD 多线程 ---的记录 iOS

    先写一个GCD static UserInfoVoModel *userInfoShare = nil; +(instancetype)shareUserInfoVoModel { static di ...

  5. OSGI框架中通过BundleContext对象对服务的注册与引用

    BundleActivator 在每个Bundle新建时都会默认生成Activator类,该类实现了BundleActivator类,实现了其start()和stop()两个方法 BundleCont ...

  6. CSS3高级选择器

    CSS3中添加了一些新的选择器 与之前的不同 这些选择器有些类似于jquery的选择器 能够让我们更高的操作DOM 废话不多说 为了更直观的了解 我们以这段为实例 来进行操作 <!DOCTYPE ...

  7. 一步一步在Windows中使用MyCat负载均衡 上篇

    传统关系型数据库的分布式开发通常需要自己做,不仅耗时耗力而且效果不是很理想,当想快速搭建时,最初想到的是看有没有第三方,网上牛人还是很多的,做得比较好的其中之一Mycat,它是开源的分布式数据库系统, ...

  8. kbhit()的三个测试

    #include <stdio.h> #include<conio.h> #include<stdlib.h> int main() { ; system(&quo ...

  9. ctags使用说明详解

    ctags使用说明详解 一.ctags是干什么的 ctags的功能:扫描指定的源文件,找出其中所包含的语法元素,并将找到的相关内容记录下来. 我用的是Exuberant Ctags 二.ctags可以 ...

  10. devexpress表格控件gridcontrol特殊应用(一)——实现禁用特定行(附源代码)

    一些特殊的项目中会存在一些特殊需求,如需要禁用特定行.这时候gridcontrol的一般属性是实现不了的,就需要做一些更改.这时候你就需要去devexpress官网中找寻些资料(官网https://w ...