一、HTTP简介
HTTP是一个属于应用层的面向对象的协议,由于其简捷、快速的方式,适用于分布式超媒体信息系统。它于1990年提出,经过几年的使用与发展,得到不断地完善和扩展。
1.1 HTTP协议的主要特点
- 支持C/S(客户/服务器)模式。
- 简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST,每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
- 灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。
- 无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
- 无状态:HTTP协议是无状态协议,无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
1.2 HTTP URL 的格式
http://host[":"port][abs_path]
http表示要通过HTTP协议来定位网络资源;host表示合法的Internet主机域名或者IP地址;port指定一个端口号,为空则使用默认端口80;abs_path指定请求资源的URI(Web上任意的可用资源)。
HTTP有两种报文分别是请求报文和响应报文,让我们先来看看请求报文。
二、 HTTP报文
2.1 请求报文
通常来说一个HTTP请求报文由请求行、请求报头、空行、和请求数据4个部分组成。
<request line>:<method> <request-URL> <version> //http请求行,用来说明请求类型、要访问的资源以及使用的HTTP版本
<headers> //http请求消息报头,用来说明服务器要使用的附加信息
<blank line> //回车换行
[<request-body>] //http请求正文
2.1.1 请求行
请求行由请求方法,URL字段和HTTP协议的版本组成,格式如下:
Method Request-URI HTTP-Version CRLF
- method(请求方法):客户端希望服务器对资源执行的动作
- GET:请求获取Request-URI所标识的资源
- POST:在Request-URI所标识的资源后附加新的数据
- HEAD:请求获取由Request-URI所标识的资源的响应消息报头
- PUT: 请求服务器存储一个资源,并用Request-URI作为其标识
- DELETE :请求服务器删除Request-URI所标识的资源
- TRACE : 请求服务器回送收到的请求信息,主要用于测试或诊断
- CONNECT: HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。
- OPTIONS :请求查询服务器的性能,或者查询与资源相关的选项和需求
request-URL(请求URL):命名了所请求资源,或者URL路径组件的完整URL
version(版本):报文所使用的HTTP版本
其格式:HTTP/.
其中major(主要版本号)和minor(次要版本号)都是整数CRLF表示回车和换行
(除了作为结尾的CRLF外,不允许出现单独的CR或LF字符)
HTTP请求方法有8种,分别是GET、POST、DELETE、PUT、HEAD、TRACE、CONNECT 、OPTIONS。其中PUT、DELETE、POST、GET分别对应着增删改查,对于移动开发最常用的就是POST和GET了
2.1.2 请求报头
在请求行之后会有0个或者多个请求报头,每个请求报头都包含一个名字和一个值,它们之间用“:”分割。
请求头部会以一个空行,发送回车符和换行符,通知服务器以下不会有请求头。
关于请求报头,会在后面的消息报头一节做统一的解释
2.1.3 请求数据 entity-body
请求数据不在GET方法中使用,而是在POST方法中使用。
POST方法适用于需要客户填写表单的场合,与请求数据相关的最常用的请求头是Content-Type和Content-Length。
2.2 响应报文
HTTP的响应报文由状态行、消息报头、空行、响应正文组成
<method> <status> <reason-phrase>
<headers>
<entity-body>
2.2.1 状态行
状态行格式如下:
HTTP-Version Status-Code Reason-Phrase CRLF
HTTP-Version表示服务器HTTP协议的版本
Status-Code表示服务器发回的响应状态代码
状态代码有三位数字组成,第一个数字定义了响应的类别,且有五种可能取值- 100~199:指示信息,表示请求已接收,继续处理
- 200~299:请求成功,表示请求已被成功接收、理解、接受
- 300~399:重定向,要完成请求必须进行更进一步的操作
- 400~499:客户端错误,请求有语法错误或请求无法实现
- 500~599:服务器端错误,服务器未能实现合法的请求
Reason-Phrase表示状态代码的文本描述
常见的状态码如下:
- 200 OK:客户端请求成功
- 400 Bad Request:客户端请求有语法错误,不能被服务器所理解
- 401 Unauthorized:请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用
- 403 Forbidden:服务器收到请求,但是拒绝提供服务
- 500 Internal Server Error:服务器发生不可预期的错误
- 503 Server Unavailable:服务器当前不能处理客户端的请求,一段时间后可能恢复正常
2.3 信息报头
- 消息报头分为通用报头、请求报头、响应报头、实体报头等
- 消息头由键值对组成,每行一对,关键字和值用英文冒号“:”分隔。
- 由一个空行(CRLF)结束的,表示了首部列表的结束和实体主体部分的开始
2.3.1 报头的通用类字段
既可以出现在请求报头,也可以出现在响应报头中
Tables | Are |
---|---|
Date: Tue,30oct 1997 02:16:03 GMT | 表示消息产生的日期和时间 |
Connection | 允许发送指定连接的选项,例如指定连接是连续的,或者指定“close”选项,通知服务器,在响应完成后,关闭连接 |
Cache-Control | Cache-Control:max-age=0报文传送缓存最大时间:用于指定缓存指令,缓存指令是单向的(响应中出现的缓存指令在请求中未必会出现),且是独立的(一个消息的缓存指令不会影响另一个消息处理的缓存机制) |
2.3.2 报头的请求类字段
请求报头通知服务器关于客户端求求的信息
Tables | Are |
---|---|
Accept: * /* | Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,/;q=0.8 客户端可识别的内容类型列表,用于指定客户端接收那些类型的信息 |
Accept: image/gif, image/jpeg, text/html | 客户端可以接收GIF图片和JPEG图片以及HTML |
Accept-Encoding:gzip,deflate,sdch | 客户端可识别的数据编码 |
Accept-Language | Accept-Language:zh-CN,zh;q=0.8,ja;q=0.6,en;q=0.4表示浏览器所支持的语言类型 |
Accept-Charset | 告诉服务器能够发送哪些字符集 |
Connection | 允许客户端和服务器指定与请求/响应连接有关的选项,例如这是为Keep-Alive则表示保持连接 |
Transfer-Encoding | 告知接收端为了保证报文的可靠传输,对报文采用了什么编码方式 |
host | 请求服务器的主机名和端口号:请求的主机名,允许多个域名同处一个IP地址,即虚拟主机 |
User-Agent | 应用程序名称:发送请求的浏览器类型、操作系统等信息 |
2.3.3 报头的响应类字段
用于服务器传递自身信息的响应
Tables | Are |
---|---|
Access-Control-Allow-Origin # Access-Control-Allow-Origin:* | 是否支持跨域请求 |
Date | Date:Wed, 26 Mar 2014 12:08:40 GMT 响应时显示报文创建的时间 |
Server | Server:nginx/1.4.4服务器的 |
ETag | 主体文件的标识符,譬如七牛就用这个判断文件的唯一性,而且还给了算法ETag:”FqYBjMdmt-xBSJNh4uyRQ7NpB2T3” |
Via | 这个是报分经过的代理和网关,例如如下我用的七牛云存储的 X-Via:1.1 lysx179:88 (Cdn Cache Server V2.0), 1.1 czdx85:8104 (Cdn Cache Server V2.0), 1.1 bjja97:10 (Cdn Cache Server V2.0) |
Connection | Connection:Close 请求连接关闭,百度等网站常见 |
If-None-Match | 如果Etag改变了,才执行 |
Location | 用于重定向接受者到一个新的位置,常用在更换域名的时候 |
Vary | Vary:Cookie |
2.3.4 报头的实体类字段
- 实体报头用来定于被传送资源的信息,既可以用于请求也可用于响应。
- 请求和响应消息都可以传送一个实体
Tables | Are |
---|---|
Content-Type | 发送给接收者的实体正文的媒体类型 |
Content-type: image/gif | 实体的主体部分是一个GIF的图片 |
Content-Type:text/html; charset=”UTF-8” | 实体的主体部分是一个文本 |
Content-length: 1024 | 实体的主体部分包含了1024字节的数据 |
Content-Encoding:gzip | 实体报头被用作媒体类型的修饰符,它的值指示了已经被应用到实体正文的附加内容的编码,因而要获得Content-Type报头域中所引用的媒体类型,必须采用相应的解码机制 |
Content-Language | 描述资源所用的自然语言,没有设置则该选项则认为实体内容将提供给所有的语言阅读 |
Content-Disposition | 主题文件描述Content-Disposition:inline; filename=”shBrushVb.js” |
Expires | 主体不在有效,从原始端再次获取主体的日期和时间 |
Last-Modified | 实体最后一次被修改日期和时间 |
2.4 示例
要想查看网页或者手机请求网络的请求报文和响应报文有很多种方法,这里推荐采用Fiddler,在Android利用Fiddler进行网络数据抓包这篇文章中详尽介绍了如何使用Fiddler,在这里就不赘述了
2.4.1 用浏览器访问CSDN博客网站
请求报文:
GET http://blog.csdn.net/itachi85 HTTP/1.1 //请求行
Host: blog.csdn.net //请求报头
Connection: keep-alive
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.80 Safari/537.36 QQBrowser/9.3.6872.400
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8
Cookie: bdshare_firstime=1443768140949; uuid_tt_dd=5028529250430960147_20151002;
...省略
很容易看出访问的是我的博客地址http://blog.csdn.net/itachi85,请求的方法是GET,因为是GET方法所以并没有请求数据。
响应报文:
HTTP/1.1 200 OK //状态行
Server: openresty //响应报头
Date: Sun, 27 Mar 2016 08:26:54 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Keep-Alive: timeout=20
Vary: Accept-Encoding
Cache-Control: private
X-Powered-By: PHP 5.4.28
Content-Encoding: gzip
//不能省略的空格
28b5
}ysI 1ߡFsgl n- ]{^_ { 'z! C , m# 0 !l ` 4x ly .ݪ*
ڴzAt_Xl * 9'O ɬ ' ק 3 ^1a
...省略
2.4.2 手机网络代理
请求报文:
POST http://patientapi.shoujikanbing.com/api/common/getVersion HTTP/1.1 //请求行
Content-Length: 226 //请求报头
Content-Type: application/x-www-form-urlencoded
Host: patientapi.shoujikanbing.com
Connection: Keep-Alive
User-Agent: Mozilla/5.0 (Linux; U; Android 4.4.4; zh-cn; MI NOTE LTE Build/KTU84P) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1
Accept-Encoding: gzip
//不能省略的空格,下面是请求数据
clientversion=2_2.0.0&time=1459069342&appId=android&channel=hjwang&sessionId=0d1cee1f31926ffa8894c64804efa855101d56eb21caf5db5dcb9a4955b7fbc9&token=b191944d680145b5ed97f2f4ccf03058&deviceId=869436020220717&type=2&version=2.0.0
从请求报文的请求行来看,请求的方法是POST,请求地址为http://patientapi.shoujikanbing.com/api/common/getVersion,很显然是获取版本信息的接口。
响应报文:
HTTP/1.1 200 OK //状态行
Server: nginx //响应报头
Date: Sun, 27 Mar 2016 09:02:20 GMT
Content-Type: text/html;charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Vary: Accept-Encoding
Set-Cookie: sessionId=0d1cee1f31926ffa8894c64804efa855101d56eb21caf5db5dcb9a4955b7fbc9; expires=Mon, 28-Mar-2016 09:02:20 GMT; Max-Age=86400; path=/; domain=.shoujikanbing.com
Set-Cookie: PHPSESSID=0d1cee1f31926ffa8894c64804efa855101d56eb21caf5db5dcb9a4955b7fbc9; path=/; domain=.shoujikanbing.com
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Encoding: gzip
//不能省略的空格
17f //实体报文编码格式为gzip所以显示在这里的响应数据是乱码
mP N @ "E ? n m 1
w ( HL (1^ P nK E ѷ93'3gNLH 7P $c \ T 4a6 L:+ 1dY%$g h H +
...省略
响应报文的实体采用的编码格式为为gzip,所以在Fiddler软件中显示的是乱码。
三、Post和Get、Soap
3.1不同之处
3.1.1 参数信息与地址栏
GET提交的数据会在地址栏中显示出来,而POST提交,地址栏不会改变
- 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包的包体中。下文示例中红色字体标明的就是实际的传输数据
3.1.2 传输数据的大小
HTTP协议没有对传输的数据大小进行限制,HTTP协议规范也没有对URL长度进行限制。
- 对于GET提交时,传输数据就会受到URL长度的 限制
- GET:特定浏览器和服务器对URL长度有限制,例如 IE对URL长度的限制是2083字节(2K+35)。对于其他浏览器,如Netscape、FireFox等,理论上没有长度限制,其限制取决于操作系 统的支持。
- POST:由于不是通过URL传值,理论上数据不受 限。
- 但实际各个WEB服务器会规定对post提交数据大小进行限制,Apache、IIS6都有各自的配置
3.1.3 安全性
POST的安全性要比GET的安全性高。 信息篡改、信息泄露,伪装攻击
注意:这里所说的安全性和上面GET提到的“安全”不是同个概念。上面“安全”的含义仅仅是不作数据修 改,而这里安全的含义是真正的Security的含义,比如:通过GET提交数据,用户名和密码将明文出现在URL上,因为(1)登录页面有可能被浏览器缓存,(2)其他人查看浏览器的历史纪录,那么别人就可以拿到你的账号和密码了,除此之外,使用GET提交数据还可能会造成Cross-siterequest forgery攻击
3.1.4 内容
- get:请求参数是作为一个key/value对的序列(查询字符串)附加到URL上的
查询字符串的长度受到web浏览器和web服务器的限制(如IE最多支持2048个字符),不适合传输大型数据集同时,它很不安全 - post:请求参数是在http标题的一个不同部分(名为entitybody)传输的,这一部分用来传输表单信息
因此必须将Content-type设置为:application/x-www-form-urlencoded。post设计用来支持web窗体上的用户字段,其参数也是作为key/value对传输。
但是:它不支持复杂数据类型,因为post没有定义传输数据结构的语义和规则。 - soap:是http post的一个专用版本,遵循一种特殊的xml消息格式
Content-type设置为: text/xml 任何数据都可以xml化
3.2、Get和POST、SOAP报文实例
Get
GET /books/?sex=man&name=Professional HTTP/1.1
Host: www.wrox.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6)
Gecko/20050225 Firefox/1.0.1
Connection: Keep-AlivePost
POST / books HTTP/1.1
Host: www.wrox.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6)
Gecko/20050225 Firefox/1.0.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 40
Connection: Keep-Alive
(—-此处空一行,以下为正文—-)
sex=man&name=ProfessionalSOAP
POST /DEMOWebServices2.8/Service.asmx HTTP/1.1
Host: api.efxnow.com
Content-Type: application/soap+xml; charset=utf-8
Content-Length: length
(—-此处空一行,以下为正文—-)
<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelopexmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:xsd="http://www.w3.org/2001/XMLSchema"xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
<soap12:Body>
<CancelOrder xmlns="https://api.efxnow.com/webservices2.3">
<UserID>string</UserID>
<PWD>string</PWD>
<OrderConfirmation>string</OrderConfirmation>
</CancelOrder>
</soap12:Body>
</soap12:Envelope>
四、浏览器缓存之Expires Etag Last-Modified max-age详解
Expires
http/1.0中定义的header,是最基础的浏览器缓存处理,表示资源在一定时间内从浏览器的缓存中获取资源,不需要请求服务器获取资源,从而达到快速获取资源,缓解服务器压力的目的。
在response的header中的格式为:Expires: Thu, 01 Dec 1994 16:00:00 GMT (必须是GMT格式)
- 应用:
- 可以在html页面中添加 来给页面设置缓存时间。
- 对于图片、css等文件则需要在IIS或者apache等运行容器中进行规则配置来让容器在请求资源的时候添加在responese的header中。
Last-modified
望文知义,根据这个词条的直译应该是上次修改(时间),通过修改服务器端的文件后再请求,发现response的header中的Last-modified改变了
更新原理
在浏览器首次请求某个资源时,服务器端返回的状态码是200 (ok),内容是你请求的资源,同时有一个Last-Modified的属性标记(Reponse Header),标识此文件在服务期端最后被修改的时间,格式:Last-Modified:Tue, 24 Feb 2009 08:01:04 GMT
浏览器第二次请求该资源时,根据HTTP协议的规定,浏览器会向服务器传送If-Modified-Since报头(Request Header),询问该文件是否在指定时间之后有被修改过,格式为:If-Modified-Since:Tue, 24 Feb 2009 08:01:04 GMT
如果服务器端的资源没有变化,则服务器返回304状态码(Not Modified),内容为空,这样就节省了传输数据量。当服务器端代码发生改变,则服务器返回200状态码(ok),内容为请求的资源,和第一次请求资源时类似。从而保证在资源没有修改时不向客户端重复发出资源,也保证当服务器有变化时,客户端能够及时得到最新的资源。
注:如果If-Modified-Since的时间比服务器当前时间(当前的请求时间request_time)还晚,会认为是个非法请求
ETag
http/1.1 中增加的header,HTTP协议规格说明定义ETag为“被请求变量的实体值” 。另一种说法是,ETag是一个可以与Web资源关联的记号(token)。典型的Web资源可以一个Web页,但也可能是JSON或XML文档。服务器单独负责判断记号是什么及其含义,并在HTTP响应头中将其传送到客户端。
ETag的格式
不同类型的Web服务器生成ETag的策略以及生成的格式是不同的:
1. apache1.3和2.x的Etag格式是:inode-size-timestamp。
2. IIS5.0和6.0的Etag格式为Filetimestamp:Changenumber。
更新原理
当浏览器首次请求资源的时候,服务器会返回200的状态码(ok),内容为请求的资源,同时response header会有一个ETag标记,该标记是服务器端根据容器(IIS或者Apache等等)中配置的ETag生成策略生成的一串唯一标识资源的字符串,ETag格式为 ETag:”856247206”
当浏览器第2次请求该资源时,浏览器会在传递给服务器的request中添加If-None-Match报头,询问服务器改文件在上次获取后是否修改了,报头格式:If-None-Match:”856246825”
服务器在获取到浏览器的请求后,会根据请求的资源查找对应的ETag,将当前服务器端指定资源对应的Etag与request中的If-None-Match进行对比,如果相同,说明资源没有修改,服务器返回304状态码(Not Modified),内容为空;如果对比发现不相同,则返回200状态码,同时将新的Etag添加到返回浏览器的response中。
max-age
Cache-Control中设置资源在本地缓存时间的一个值,单位为:秒(s),其他值还有private、no-cache、must-revalidate等
几者之间的关系
Expires 与max-age
Expires存在HTTP 1.0 版本, 标识本地缓存的截止时间,允许浏览器在这个时间之前不去向服务器端发送请求验证资源是否有更新
max-age是HTTP 1.1版本新增的, 标识资源可以在本地缓存多少秒,存储的是更新间隔。
**Expires 的一个缺点就是,返回的到期时间是服务器端的时间,这样存在一个问题,如果浏览器所在机器的时间与服务器的时间相差很大,那么误差就很大,所以在HTTP 1.1版开始,使用Cache-Control: max-age替代。
注: 如果max-age和Expires同时存在,则被Cache-Control的max-age覆盖。**
Expires =max-age + “每次下载时的当前的request时间”
所以一旦重新下载的页面后,expires就重新计算一次,但last-modified不会变化
Last-Modified和Expires
使用Last-Modified标识由于在资源未修改时返回的response内容为空,可以节省一点带宽,但是还是逃不掉发一个HTTP请求出去,需要浏览器连接一次服务器端。
而Expires标识却使得浏览器干脆连HTTP请求都不用发,但是当用户使用F5或者点击Refresh按钮的时候,就算URI设置了Expires,浏览器一样也会发一个HTTP请求给服务器端,所以,Last-Modified还是要用的,而且要和Expires一起用
Etag和Expires
和 Last-Modified和Expires的情况类似,需要Expires控制请求的频率,Etag在强制刷新时作为保障
Last-Modified和Etag
分布式系统里多台机器间文件的last-modified必须保持一致,以免负载均衡到不同机器导致比对失败,一般建议分布式系统尽量关闭掉Etag(每台机器生成的etag都会不一样)
Last-Modified和ETags请求的http报头一起使用,服务器首先产生Last-Modified/Etag标记,服务器可在稍后使用它来判断页面是否已经被修改,来决定文件是否继续缓存
过程如下:
1.客户端请求一个页面(A)。
2.服务器返回页面A,并在给A加上一个Last-Modified/ETag。
3.客户端展现该页面,并将页面连同Last-Modified/ETag一起缓存。
4.客户再次请求页面A,并将上次请求时服务器返回的Last-Modified/ETag一起传递给服务器。
5.服务器检查该Last-Modified或ETag,并判断出该页面自上次客户端请求之后还未被修改,直接返回响应304和一个空的响应体。
从资源更新原理来看Last-Modified和Etag基本是类似的,那为什么http协议中要搞2个标识呢?
Last-Modified存在的问题:
1、在集群服务器上各个服务器上的文件时间可能不同。
2、如果用旧文件覆盖新文件,因为时间更前,浏览器不会请求这个更旧的文件。
3、时间精度为s级,对文件修改精度有严格要求的场景不能满足