HTTP 协议概念
HTTP 协议是 Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web )服务器传输超文本到本地浏览器的传送协议。
HTTP 是一个基于 TCP/IP 通信协议来传递数据(HTML 文件, 图片文件, 查询结果等)。
HTTP 是一个属于应用层的面向对象的协议,由于其简捷、快速的方式,适用于分布式超媒体信息系统。它于 1990 年提出,经过几年的使用与发展,得到不断地完善和扩展。目前在 WWW 中使用的是 HTTP/1.0 的第六版,HTTP/1.1 的规范化工作正在进行之中,而且 HTTP-NG(Next Generation of HTTP)的建议已经提出。
HTTP 协议工作于客户端-服务端架构为上。浏览器作为 HTTP 客户端通过 URL 向 HTTP 服务端即 WEB 服务器发送所有请求。Web 服务器根据接收到的请求后,向客户端发送响应信息。
HTTP 特性
HTTP 协议构建于 TCP/IP 协议之上,是一个应用层协议,默认端口号是 80
HTTP 是无连接无状态的
HTTP 报文
请求报文
HTTP 协议是以 ASCII 码传输,建立在 TCP/IP 协议之上的应用层规范。规范把 HTTP 请求分为三个部分:状态行、请求头、消息主体。类似于下面这样:
1 | <method> |
HTTP 定义了与服务器交互的不同方法,最基本的方法有 4 种,分别是 GET,POST,PUT,DELETE。
URL 全称是资源描述符,我们可以这样认为:一个 URL 地址,它用于描述一个网络上的资源.
而 HTTP 中的 GET,POST,PUT,DELETE 就对应着对这个资源的查,增,改,删 4 个操作。
GET 用于信息获取,而且应该是安全的和幂等的。
所谓安全的意味着该操作用于获取信息而非修改信息。换句话说,GET 请求一般不应产生副作用。就是说,它仅仅是获取资源信息,就像数据库查询一样,不会修改,增加数据,不会影响资源的状态。
幂等的意味着对同一 URL 的多个请求应该返回同样的结果。
GET 请求报文示例:
1 | GET /books/?sex=man&name=Professional HTTP/1.1 Host: www.example.com User-Agent: |
POST 表示可能修改变服务器上的资源的请求。
1 | POST /books/ HTTP/1.1 Host: www.example.com User-Agent: Mozilla/5.0 (Windows; U; |
注意:
GET 可提交的数据量受到 URL 长度的限制,HTTP 协议规范没有对 URL 长度进行限制。这个限制是特定的浏览器及服务器对它的限制
理论上讲,POST 是没有大小限制的,HTTP 协议规范也没有进行大小限制,出于安全考虑,服务器软件在实现时会做一定限制
参考上面的报文示例,可以发现 GET 和 POST 数据内容是一模一样的,只是位置不同,一个在 URL 里,一个在 HTTP 包的包体里
POST 提交数据的方式
HTTP 协议中规定 POST 提交的数据必须在 body 部分中,但是协议中没有规定数据使用哪种编码方式或者数据格式。实际上,开发者完全可以自己决定消息主体的格式,只要最后发送的 HTTP 请求满足上面的格式就可以。
但是,数据发送出去,还要服务端解析成功才有意义。一般服务端语言如 PHP、Python 等,以及它们的 framework,都内置了自动解析常见数据格式的功能。服务端通常是根据请求头(headers)中的 Content-Type 字段来获知请求中的消息主体是用何种方式编码,再对主体进行解析。所以说到 POST 提交数据方案,包含了 Content-Type 和消息主体编码方式两部分。下面就正式开始介绍它们:
application/x-www-form-urlencoded
这是最常见的 POST 数据提交方式。浏览器的原生<form>
表单,如果不设置 enctype 属性,那么最终就会以 application/x-www-form-urlencoded 方式提交数据。上个小节当中的例子便是使用了这种提交方式。可以看到 body 当中的内容和 GET 请求是完全相同的。
multipart/form-data
这又是一个常见的 POST 数据提交的方式。我们使用表单上传文件时,必须让 <form>
表单的 enctype 等于 multipart/form-data。直接来看一个请求示例:
1 | POST http://www.example.com HTTP/1.1 |
这个例子稍微复杂点。首先生成了一个 boundary 用于分割不同的字段,为了避免与正文内容重复,boundary 很长很复杂。然后 Content-Type 里指明了数据是以 multipart/form-data 来编码,本次请求的 boundary 是什么内容。
消息主体里按照字段个数又分为多个结构类似的部分,每部分都是以 –boundary 开始,紧接着是内容描述信息,然后是回车,最后是字段具体内容(文本或二进制)。如果传输的是文件,还要包含文件名和文件类型信息。消息主体最后以 –boundary– 标示结束。关于 multipart/form-data 的详细定义,请前往 RFC1867 查看(或者相对友好一点的 MDN 文档)。
这种方式一般用来上传文件,各大服务端语言对它也有着良好的支持。
上面提到的这两种 POST 数据的方式,都是浏览器原生支持的,而且现阶段标准中原生 <form>
表单也只支持这两种方式(通过 <form>
元素的 enctype 属性指定,默认为 application/x-www-form-urlencoded。其实 enctype 还支持 text/plain,不过用得非常少)。
随着越来越多的 Web 站点,尤其是 WebApp,全部使用 Ajax 进行数据交互之后,我们完全可以定义新的数据提交方式,例如 application/json,text/xml,乃至 application/x-protobuf 这种二进制格式,只要服务器可以根据 Content-Type 和 Content-Encoding 正确地解析出请求,都是没有问题的。
响应报文
HTTP 响应与 HTTP 请求相似,HTTP 响应也由 3 个部分构成,分别是:
- 状态行
- 响应头(Response Header)
- 响应正文
状态行由协议版本、数字形式的状态代码、及相应的状态描述,各元素之间以空格分隔。
分类 | 分类描述 |
---|---|
1** | 信息,服务器收到请求,需要请求者继续执行操作 |
2** | 成功,操作被成功接收并处理 |
3** | 重定向,需要进一步的操作以完成请求 |
4** | 客户端错误,请求包含语法错误或无法完成请求 |
5** | 服务器错误,服务器在处理请求的过程中发生了错误 |
常见的状态码有如下几种:
- 200 OK 客户端请求成功
- 301 Moved Permanently 请求永久重定向
- 302 Moved Temporarily 请求临时重定向
- 304 Not Modified 文件未修改,可以直接使用缓存的文件。
- 400 Bad Request 由于客户端请求有语法错误,不能被服务器所理解。
- 401 Unauthorized 请求未经授权。这个状态代码必须和 WWW-Authenticate 报头域一起使用
- 403 Forbidden 服务器收到请求,但是拒绝提供服务。服务器通常会在响应正文中给出不提供服务的原因
- 404 Not Found 请求的资源不存在,例如,输入了错误的 URL
- 500 Internal Server Error 服务器发生不可预期的错误,导致无法完成客户端的请求。
- 503 Service Unavailable 服务器当前不能够处理客户端的请求,在一段时间之后,服务器可能会恢复正常。
下面是一个 HTTP 响应的例子:
1 | HTTP/1.1 200 OK Server:Apache Tomcat/5.0.12 Date:Mon,6Oct2003 13:23:42 GMT |
条件 GET
HTTP 条件 GET 是 HTTP 协议为了减少不必要的带宽浪费,提出的一种方案。详见 RFC2616 。
1.HTTP 条件 GET 使用的时机?
客户端之前已经访问过某网站,并打算再次访问该网站。
2.HTTP 条件 GET 使用的方法?
客户端向服务器发送一个包询问是否在上一次访问网站的时间后是否更改了页面,如果服务器没有更新,显然不需要把整个网页传给客户端,客户端只要使用本地缓存即可,如果服务器对照客户端给出的时间已经更新了客户端请求的网页,则发送这个更新了的网页给用户。
下面是一个具体的发送接受报文示例:
客户端发送请求:
1 | GET / HTTP/1.1 Host: www.sina.com.cn:80 If-Modified-Since:Thu, 4 Feb 2010 |
第一次请求时,服务器端返回请求数据,之后的请求,服务器根据请求中的 If-Modified-Since 字段判断响应文件没有更新,如果没有更新,服务器返回一个 304 Not Modified 响应,告诉浏览器请求的资源在浏览器上没有更新,可以使用已缓存的上次获取的文件。
1 | HTTP/1.0 304 Not Modified Date: Thu, 04 Feb 2010 12:38:41 GMT Content-Type: |
如果服务器端资源已经更新的话,就返回正常的响应。
GET 与 POST 的区别
GET 请求
1 | GET /books/?sex=man&name=Professional HTTP/1.1 Host: www.wrox.com User-Agent: |
注意最后一行是空行
POST 请求
1 | POST / HTTP/1.1 Host: www.wrox.com User-Agent: Mozilla/5.0 (Windows; U; Windows |
1.GET 提交,请求的数据会附在 URL 之后(就是把数据放置在 HTTP 协议头中),以?分割 URL 和传输数据,多个参数用&连接;例 如:login.action?name=hyddd&password=idontknow&verify=%E4%BD%A0 %E5%A5%BD。如果数据是英文字母/数字,原样发送,如果是空格,转换为+,如果是中文/其他字符,则直接把字符串用 BASE64 加密,得出如: %E4%BD%A0%E5%A5%BD,其中%XX 中的 XX 为该符号以 16 进制表示的 ASCII。
POST 提交:把提交的数据放置在是 HTTP 包的包体中。
因此,GET 提交的数据会在地址栏中显示出来,而 POST 提交,地址栏不会改变
2.传输数据的大小:首先声明:HTTP 协议没有对传输的数据大小进行限制,HTTP 协议规范也没有对 URL 长度进行限制。
而在实际开发中存在的限制主要有:
GET:特定浏览器和服务器对 URL 长度有限制,例如 IE 对 URL 长度的限制是 2083 字节(2K+35)。对于其他浏览器,如 Netscape、FireFox 等,理论上没有长度限制,其限制取决于操作系统的支持。
因此对于 GET 提交时,传输数据就会受到 URL 长度的限制。
POST:由于不是通过 URL 传值,理论上数据不受限。但实际各个 WEB 服务器会规定对 post 提交数据大小进行限制,Apache、IIS6 都有各自的配置。
3.安全性
POST 的安全性要比 GET 的安全性高。比如:通过 GET 提交数据,用户名和密码将明文出现在 URL 上,因为:
(1)登录页面有可能被浏览器缓存;
(2)其他人查看浏览器的历史纪录,那么别人就可以拿到你的账号和密码了,除此之外,使用 GET 提交数据还可能会造成 Cross-site request forgery(CSRF)攻击
4.Http get,post,soap 协议都是在 http 上运行的
(1)get:请求参数是作为一个 key/value 对的序列(查询字符串)附加到 URL 上的
查询字符串的长度受到 web 浏览器和 web 服务器的限制(如 IE 最多支持 2048 个字符),不适合传输大型数据集同时,它很不安全
(2)post:请求参数是在 http 标题的一个不同部分(名为 entity body)传输的,这一部分用来传输表单信息,因此必须将 Content-type 设置为:application/x-www-form- urlencoded。post 设计用来支持 web 窗体上的用户字段,其参数也是作为 key/value 对传输。
但是:它不支持复杂数据类型,因为 post 没有定义传输数据结构的语义和规则。
(3)soap:是 http post 的一个专用版本,遵循一种特殊的 xml 消息格式
Content-type 设置为: text/xml 任何数据都可以 xml 化。
总结
GET 提交的数据会放在 URL 之后,以?分割 URL 和传输数据,参数之间以&相连,如 EditPosts.aspx?name=test1&id=123456. POST 方法是把提交的数据放在 HTTP 包的 Body 中.
GET 提交的数据大小有限制(因为浏览器对 URL 的长度有限制),而 POST 方法提交的数据没有限制.
GET 方式需要使用 Request.QueryString 来取得变量的值,而 POST 方式通过 Request.Form 来获取变量的值。
GET 方式提交数据,会带来安全问题,比如一个登录页面,通过 GET 方式提交数据时,用户名和密码将出现在 URL 上,如果页面可以被缓存或者其他人可以访问这台机器,就可以从历史记录获得该用户的账号和密码.
HttpClient
简介
HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。HttpClient 已经应用在很多的项目中,比如 Apache Jakarta 上很著名的另外两个开源项目 Cactus 和 HTMLUnit 都使用了 HttpClient。
特性
- 基于标准、纯净的 java 语言。实现了 Http1.0 和 Http1.1
- 以可扩展的面向对象的结构实现了 Http 全部的方法(GET, POST, PUT, DELETE, HEAD, OPTIONS, and TRACE)。
- 支持 HTTPS 协议。
- 通过 Http 代理建立透明的连接。
- 利用 CONNECT 方法通过 Http 代理建立隧道的 https 连接。
- Basic, Digest, NTLMv1, NTLMv2, NTLM2 Session, SNPNEGO/Kerberos 认证方案。
- 插件式的自定义认证方案。
- 便携可靠的套接字工厂使它更容易的使用第三方解决方案。
- 连接管理器支持多线程应用。支持设置最大连接数,同时支持设置每个主机的最大连接数,发现并关闭过期的连接。
- 自动处理 Set-Cookie 中的 Cookie。
- 插件式的自定义 Cookie 策略
- Request 的输出流可以避免流中内容直接缓冲到 socket 服务器。
- Response 的输入流可以有效的从 socket 服务器直接读取相应内容。
- 在 http1.0 和 http1.1 中利用 KeepAlive 保持持久连接。
- 直接获取服务器发送的 response code 和 headers。
- 设置连接超时的能力。
- 实验性的支持 http1.1 response caching。
- 源代码基于 Apache License 可免费获取。
使用方法
使用 HttpClient 发送请求、接收响应很简单,一般需要如下几步即可。
- 创建 HttpClient 对象。
- 创建请求方法的实例,并指定请求 URL。如果需要发送 GET 请求,创建 HttpGet 对象;如果需要发送 POST 请求,创建 HttpPost 对象。
- 如果需要发送请求参数,可调用 HttpGet、HttpPost 共同的 setParams(HetpParams params)方法来添加请求参数;对于 HttpPost 对象而言,也可调用 setEntity(HttpEntity entity)方法来设置请求参数。
- 调用 HttpClient 对象的 execute(HttpUriRequest request)发送请求,该方法返回一个 HttpResponse。
- 调用 HttpResponse 的 getAllHeaders()、getHeaders(String name)等方法可获取服务器的响应头;调用 HttpResponse 的 getEntity()方法可获取 HttpEntity 对象,该对象包装了服务器的响应内容。程序可通过该对象获取服务器的响应内容。
- 释放连接。无论执行方法是否成功,都必须释放连接