HTTP协议简介
①HTTP(hypertext transport protocol),即超文本传输协议。这个协议详细规定了浏览器和万维网服务器之间互相通信的规则。
②客户端与服务端通信时传输的内容我们称之为报文。
③HTTP就是一个通信规则,这个规则规定了客户端发送给服务器的报文格式,也规定了服务器发送给客户端的报文格式。实际我们要学习的就是这两种报文。客户端发送给服务器的称为”请求报文“,服务器发送给客户端的称为”响应报文“。
报文的总体结构
首行
头
空行
体
①空行的作用是分割首部和体,而get请求没有请求体,所以也看不到空行。
②get请求通过url地址来发送请求参数所以没有请求体,而post请求通过请求体发送请求参数
③除了表单的metod属性设置为post时,其余的全都是get请求。
GET请求报文源码
GET /05_WEB_HTTP/target.html?username=admin HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
X-HttpWatch-RID: 19630-10035
Referer: http://localhost:8080/05_WEB_HTTP/form.html
Accept-Language: zh-CN
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
Accept-Encoding: gzip, deflate
Host: localhost:8080
DNT: 1
Connection: Keep-Alive
请求报文的结构剖析:
请求首行
GET /05_WEB_HTTP/target.html?username=admin&password=123123 HTTP/1.1
请求方式 资源路径 ? 查询字符串 协议名/协议版本
请求头:
通过观察,发现请求头实际上是一个一个的键值对结构,有的是一个键对应一个值,有的是一个键对应多个值
Accept: text/html, application/xhtml+xml, */*
浏览器允许的内容:text/html表示允许网页,这种值我们称为MIME值,*/*表示任意类型(浏览器告诉服务器给我返回什么类型的数据)
X-HttpWatch-RID: 19630-10035
Referer: http://localhost:8080/05_WEB_HTTP/form.html
请求的来源:广告的计费,防止盗链
Accept-Language: zh-CN
允许的语言:zh-CN表示的中文
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
用户代理(用户的浏览器信息),用来帮助服务器识别不同的浏览器的
Accept-Encoding: gzip, deflate
Host: localhost:8080
主机地址:
DNT: 1
Connection: Keep-Alive
连接的时间:Keep-Alive表示一个长连接,告诉服务器,你别着急断开连接,等我一会,等我将资源都加载完了以后在断开吧
POST请求报文源码
POST /05_WEB_HTTP/target.html HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
X-HttpWatch-RID: 19630-10056
Referer: http://localhost:8080/05_WEB_HTTP/form.html
Accept-Language: zh-CN
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
Host: localhost:8080
Content-Length: 14
DNT: 1
Connection: Keep-Alive
Cache-Control: no-cache
username=admin
username=%E5%AD%99%E6%82%9F%E7%A9%BA
post请求报文的结构剖析
请求首行
POST /05_WEB_HTTP/target.html HTTP/1.1
请求头
Accept: text/html, application/xhtml+xml, */*
X-HttpWatch-RID: 19630-10056
Referer: http://localhost:8080/05_WEB_HTTP/form.html
Accept-Language: zh-CN
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
Content-Type: application/x-www-form-urlencoded
请求体的类型: application/x-www-form-urlencoded 表示请求体中的内容将会进行URL编码
url编码指的就是将请求参数中的内容有普通字符转换为二进制字符的过程
url编码会先将字符转换为二进制,在将二进制转换为十六进制(因为二进制显示出来比较长)
Accept-Encoding: gzip, deflate
Host: localhost:8080
Content-Length: 14
DNT: 1
Connection: Keep-Alive
Cache-Control: no-cache
空行
空行用于分隔请求首部和请求体
请求体
username=admin
请求体用来传递用户的请求参数
响应报文源码
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Accept-Ranges: bytes
ETag: W/"163-1460770549797"
Last-Modified: Sat, 16 Apr 2016 01:35:49 GMT
Content-Type: text/html
Content-Length: 163
Date: Sat, 16 Apr 2016 01:36:37 GMT
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>我是一个网站的首页</h1>
</body>
</html>
响应报文的结构剖析
响应首行
HTTP/1.1 200 OK
协议/协议版本 响应状态码 响应状态码的描述
200 表示响应成功 所有2开头都是成功相关的
404 表示资源未找到 所有以4开头都是客户端的错
500 表示服务器内部错误 所有5开头都是服务器的错
302 表示请求的重定向 所有3开头的都是重定向相关的(京东的短域名 3.cn)
响应头
Server: Apache-Coyote/1.1
Accept-Ranges: bytes
ETag: W/"163-1460770549797"
Last-Modified: Sat, 16 Apr 2016 01:35:49 GMT
Content-Type: text/html
响应体的类型:text/html表示响应体是一个网页(此属性用于响应(response)浏览器时,告诉浏览器你是用什么字符集编码,这样浏览器根据你反应的字符集进行解码,就不会乱码)
Content-Length: 163
Date: Sat, 16 Apr 2016 01:36:37 GMT
格林威日的标准时间
空行
空行用来分割响应首部和响应体
响应体
响应体就是服务器要发送给浏览器内容
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>我是一个网站的首页</h1>
</body>
</html>
get请求和post请求
1.除了表单的method属性设置为post时,其余的都是get请求
2.get请求通过url地址来传递请求参数的,请求参数可以直接在地址栏看见,不太安全
post请求通过请求体来传递请求参数的,请求参数不能直接显示,相对安全
3.get请求通过url地址来传递请求参数的,而url地址限制请求参数的长度为255个字符,长度有限
post请求通过请求体来传递请求参数的,大小没有限制。
结论:在开发中如果没有特殊情况,表单中均使用post请求。
response.setHeader("content-type", "text/html;charset=UTF-8"); //告诉浏览器使用什么字符编码
response.getWriter().write(result);
乱码
产生乱码的根本原因
> 浏览器和服务器之间的通信是基于请求和响应的
> 发送请求和响应时,需要将请求和响应中的内容转换为二进制编码
> 在读取请求和响应时,需要将内容从二进制编码转换为字符。
>
将字符转换为二进制编码的过程我们称为编码
>
将二进制编码转换为字符的过程我们称为解码
>
编码和解码所采用的规则我们称为字符集
> 产生乱码的根本原因就是编码和解码所使用的字符编码不同
解决办法
统一编码和解码所使用的字符编码为utf-8
常见的字符编码
ASKII:美国
ISO-8859-1:欧洲
GBK:中国
GB2312:中国
UTF-8:万国码,全球通用
接下来我们从请求编码和响应编码深度剖析一下产生乱码的原因 以及解决方案
1.请求编码:请求是浏览器发送给服务器的,此时是浏览器编码,服务器解码
a. 浏览编码
浏览器编码的规则:浏览器会自动使用当前页面所采用的字符集来编码
utf-8的孙悟空:%E5%AD%99%E6%82%9F%E7%A9%BA
gb2312的孙悟空: %CB%EF%CE%F2%BF%D5
因为网页是我们自己写的,所以只需要将我们项目中所有的页面编码都设置为utf-8,那么浏览器永远都会使用utf-8来编码
b. 服务器解码:
post请求
post请求默认在Servlet中解码,通过HttpServletRequest对象进行解码的
而request对象默认字符集是iso-8859-1,而这哥们不支持中文
所以只需要修改request的字符集为utf-8
解决方案:
在request.getParameter()方法第一次调用之前调用:
request.setCharacterEncoding("utf-8")
get请求
get请求通过url地址发送的请求参数,参数会被Tomcat服务器自动解码。
而Tomcat默认使用iso-88591-1解码
解决方案:
修改Tomcat的默认解码字符集为utf-8
在项目的映射文件中的server.xml文件中在Connector标签添加如下属性URIEncoding="utf-8"
2.响应编码:响应是服务器发送给浏览器的,此时是服务器编码,浏览器解码
a.服务器编码
服务器编码实际上是由response编码,可不可以指定一下response的编码字符集呢?
解决方案:
response.setCharacterEncoding("utf-8");
b.浏览器解码
浏览器默认使用的是gb2312解码,所以即使设置了编码,浏览器上显示的还是乱码。
所以我们还需要告诉浏览器,服务器所使用的字符集,可以通过一个响应头来告诉浏览器字符集
Content-Type:text/html;charset=utf-8
解决方案:
在向浏览器发送响应之前,调用如下代码:告诉浏览器解码方式
response.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
1、一个http请求经过的几个环节:
浏览器(ie firefox)【get/post】------------>Servlet服务器------------------------------->浏览器显示
编码
解码成unicode,然后将显示的内容编码
解码
(1) 浏览器把URL(以及post提交的内容)经过编码后发送给服务器。
(2) 这里的Servlet服务器实际上指的是由Servlet服务器提供的servlet实现ServletRequestWrapper,不同应用服务器的 servlet实现不同,这些servlet的实现把这些内容解码转换为unicode,处理完毕后,然后
再把结果(即网页)编码返回给浏览器。
(3) 浏览器按照指定的编码显示该网页。
当对字符串进行编码和解码的时候都涉及到字符集,通常使用的字符集为ISO8859-1、GBK、UTF-8、UNICODE。
2、Servlet API
我们使用以下servlet API获得URL的值及参数。
request.getParameter("name");
// 获得queryString的参数值(来自于get和post),其值经过Servlet服务器URL Decode过的(默认是通过ISO-8859-1解码的)
request.getPathInfo();
// 注意:pathinfo返回的字符串是经过Servlet服务器URL Decode过的。
requestURI = request.getRequestURI(); // 内容为:contextPath/servletPath/pathinfo 浏览器提交过来的原始数据,未被Servlet服务器URL Decode过。
3、开发人员必须清楚的servlet规范:
(1) HttpServletRequest.setCharacterEncoding()方法 仅仅只适用于设置post提交的request body的编码而不是设置get方法提交的queryString的编码。
该方法告诉request应该采用什么编码解析post
传过来的内容。很多文章并没 有说明这一点。get请求是服务器解码所以应该修改tomcat server.xml里的字符集为UTF-8
(2) HttpServletRequest.getPathInfo()返回的结果是由Servlet服务器解码(decode)过的。
(3) HttpServletRequest.getRequestURI()返回的字符串没有被Servlet服务器decoded过。
(4) POST提交的数据是作为request body的一部分。
(5) 网页的Http头中ContentType("text/html; charset=GBK")的作用:
(a) 告诉浏览器网页中数据是什么编码;
(b) 表单提交时,通常浏览器会根据ContentType指定的charset对表单中的数据编码,然后发送给服务器的。
这里需要注意的是:这里所说的ContentType是指http头的ContentType,而不是在网页中meta中的ContentType。
下面我们分别从浏览器和应用服务器来举例说明:
汉字
编码
二进制表示
中国
UTF-8
0xe4 0xb8 0xad 0xe5 0x9b 0xbd[-28, -72, -83, -27, -101, -67]
中国
GBK
0xd6 0xd0 0xb9 0xfa[-42, -48, -71, -6]
中国
ISO8859-1 0x3f,0x3f[63, 63]信息失去
(一)、浏览器
1、GET方式提交,浏览器会对URL进行URL encode,然后发送给服务器。
(1) 对于中文IE,如果在高级选项中选中总以UTF-8发送(默认方式),则PathInfo是URL Encode是按照UTF-8编码,QueryString是按照GBK编码。
实际上提交是:
GET /example/中国?name=�й�
(2) 对于中文IE,如果在高级选项中取消总以UTF-8发送,则PathInfo和QueryString是URL encode按照GBK编码。
实际上提交是:
GET /example/�й�?name=�й�
(3) 对于中文firefox,则pathInfo和queryString都是URL encode按照GBK编码。
实际上提交是:
GET /example/�й�?name=�й�
很显然,不同的浏览器以及同一浏览器的不同设置,会影响最终URL中PathInfo的编码。对于中文的IE和FIREFOX都是采用GBK编码QueryString。
小结:解决方案:
1、URL中如果含有中文等非ASCII字符,则浏览器会对它们进行URLEncode。为了避免浏览器采用了我们不希望的编码,所以最好不要在URL中直接使用非ASCII字符,而采用URL Encode编码过的字符串%.
比如:
建议:
2、我们建议URL中PathInfo和QueryString采用相同的编码,这样对服务器端处理的时候会更加简单。
4、POST提交
对于POST方式,表单中的参数值对是通过request body发送给服务器,此时浏览器会根据网页的ContentType("text/html; charset=GBK")中指定的编码进行对表单中的数据进行编码,然后发给服务器。在服务器端的程序中我们可以通过Request.setCharacterEncoding() 设置编码,然后通过request.getParameter获得正确的数据。
解决方案:
从最简单,所需代价最小来看,我们对URL以及网页中的编码使用统一的编码对我们来说是比较合适的。
如果不使用统一编码的话,我们就需要在程序中做一些编码转换的事情。这也是我们为什么看到有网络上大量的资料介绍如何对乱码进行处理,其中很多解决方案都只是一时的权宜之计,没有从根本上解决问题。
(二)、Servlet服务器
Servlet服务器实现的Servlet遇到URL和POST提交的数据中含有%的字符串,它会按照指定的字符集解码。下面两个Servlet方法返回的结果都是经过解码的:
request.getParameter("name");
request.getPathInfo();
这里所说的"指定的字符集"是在应用服务器的配置文件中配置。
(1) tomcat服务器
对于tomcat服务器,该文件是server.xml
<Connector port="8080" protocol="HTTP/1.1"
maxThreads="150" connectionTimeout="20000"
redirectPort="8443" URIEncoding="GBK"/>
URIEncoding告诉服务器servlet解码URL时采用的编码。
<Connector port="8080" ... useBodyEncodingForURI="true" />
useBodyEncodingForURI告诉服务器解码URL时候需要采用request body指定的编码。
(三)浏览器显示
浏览器根据http头中的ContentType("text/html; charset=GBK"),指定的字符集来解码服务器发送过来的字节流。我们可以调用 HttpServletResponse.setContentType()设置http头的ContentType。
总结:
1、URL中的PathInfo和QueryString字符串的编码和解码是由浏览器和应用服务器的配置决定的,我们的程序不能设置,不要期望用request.setCharacterEncoding()方法能设置URL中参数值解码时的字符集。
所以我们建议URL中不要使用中文等非ASCII字符,如果含有非ASCII字符的话要使用URLEncode编码一下,比如:
正确的写法:
并且我们建议URL中不要在PathInfo和QueryString同时使用非ASCII字符,比如
原因很简单:不同浏览器对URL中PathInfo和QueryString编码时采用的字符集不同,但应用服务器对URL通常会采用相同的字符集来解码。
2、我们建议URL中的URL Encode编码的字符集和网页的contentType的字符集采用相同的字符集,这样程序的实现就很简单,不用做复杂的编码转换。
一:get请求url中带有中文参数,有三种方式进行处理防止中文乱码
1、如果使用tomcat作为服务器,那么修改tomcat配置文件conf/server.xml中,在 <Connector port="8082" protocol="HTTP/1.1" 中加入 URIEncoding="utf-8"的编码集
2、前台需要对中文参数进行编码,调用js方法encodeURI(url),将url编码,然后请求。
后台接受时,需处理String str = new
String(request.getParameter("param").getBytes("iso8859-1"),"UTF-8")
;
原因:tomcat不设置编码时,默认是iso8859-1,即tomcat默认会以iso8859-1编码接收get参数。 以上操作是将参数以iso8859-1编码转化为字节数组,然后再以UTF-8将字节数组转化为字符串。
另外需注意在框架的使用中:request.setCharacterEncoding(encoding);只对post请求有效。而且,spring的CharacterEncodingFilter也只是做了request(和response).setCharacterEncoding(encoding);的操作。所以spring的filter配置不作用于get参数接收。
3、解决get请求,后台接受中文参数乱码处理的方法(搜索功能带参数)
(1)前台获取数据,在js中进行编码处理
encodeURI函数采·用utf-8进行编码,而在服务器的进行解码时候,默认都不是以uft-8进行解码,所以就会出现乱码。两次encodeURI,第一次编码得到的是UTF-8形式的URL,第二次编码得到的依然是UTF-8形式的URL,但是在效果上相当于首先进行了一 次UTF-8编码(此时已经全部转换为ASCII字符),再进行了一次iso-8859-1编码,因为对英文字符来说UTF-8编码和ISO- 8859-1编码的效果相同。
(2)后台解码处理
在后台接收参数时候,首先通过request.getParameter()自动进行第一次解码(可能是 gb2312,gbk,utf-8,iso-8859-1等字符集,对结果无影响)得到ascii字符,然后再使用UTF-8进行第二次解码,通常使用 java.net.URLDecoder("","UTF-8")方法。
两次编码两次解码的过程为:
UTF-8编码->UTF-8(iso-8859-1)编码->iso-8859-1解码->UTF-8解码,编码和解码的过程是对称的,所以不会出现乱码