转载于金阳光测试
【背景】
在接口测试前,为什么要详细讲解http协议,一句话:“接口的本质是协议,协议中最主流的当属http(s)”。现在的互联网应用,前后端是分离的,客户端和服务器端通过接口服务进行通信,最最常见的就属restful风格的接口,其本质就是“http协议+json数据”。有人可能用过很多种http协议工具,当到一个新环境换一种新工具后就各种茫然和不习惯;也有人基于http协议组织的报文怎么也调不通过,变换点参数就歇菜了。究其原因,一来对协议规则不熟悉,导致使用工具时知其然不知其所以然,二来习惯了功能测试,对如何开展接口测试并不能了然于心。所以本篇会对http协议进行剖析,后面的篇章会接着讲解接口自动化测试框架体系。
【HTTP协议特点】
1、支持客户/服务器模式。
2、简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST。每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
3、灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。
4、无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
5、无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。当你登录到一个网站时,你的登录状态也是由Cookie或Session来“记忆”的,因为服务器并不知道你是否登录。但是从HTTP/1.1起,默认都开启了Keep-Alive,保持连接特性,简单地说,当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭,如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接。
【HTTP协议剖析】
-
一次完整HTTP请求经历的7个过程:
-
HTTP协议在TCP/IP协议栈的位置:
-
HTTP请求报文格式:
-
HTTP响应报文格式:
【Fiddler中Http报文构成】
-
fiddler中原始请求报文:
-
fiddler中原始响应报文:
【JMeter中Http报文构成】
【常见请求方法】
请求方法 |
方法描述 |
GET |
请求获取Request-URI所标识的资源 |
POST |
在Request-URI所标识的资源后附加新的数据 |
PUT |
请求服务器存储一个资源,并用Request-URI作为其标识 |
DELETE |
请求服务器删除Request-URI所标识的资源 |
HEAD |
请求获取由Request-URI所标识的资源的响应消息报头 |
TRACE |
请求服务器回送收到的请求信息,主要用于测试或诊断 |
CONNECT |
保留将来使用 |
OPTIONS |
请求查询服务器的性能,或者查询与资源相关的选项和需求 |
【常见协议头】
-
常见请求报头:
请求报头 |
描述 |
Accept |
Accept请求报头域用于指定客户端接受哪些类型的信息。eg:Accept:image/gif,表明客户端希望接受GIF图象格式的资源;Accept:text/html,表明客户端希望接受html文本。 |
Accept-Charset |
Accept-Charset请求报头域用于指定客户端接受的字符集。eg:Accept-Charset:iso-8859-1,gb2312.如果在请求消息中没有设置这个域,缺省是任何字符集都可以接受。 |
Accept-Encoding |
Accept-Encoding请求报头域类似于Accept,但是它是用于指定可接受的内容编码。eg:Accept-Encoding:gzip.deflate.如果请求消息中没有设置这个域服务器假定客户端对各种内容编码都可以接受。 |
Accept-Language |
Accept-Language请求报头域类似于Accept,但是它是用于指定一种自然语言。eg:Accept-Language:zh-cn.如果请求消息中没有设置这个报头域,服务器假定客户端对各种语言都可以接受。 |
Authorization |
Authorization请求报头域主要用于证明客户端有权查看某个资源。当浏览器访问一个页面时,如果收到服务器的响应代码为401(未授权),可以发送一个包含Authorization请求报头域的请求,要求服务器对其进行验证。 |
Host |
Host请求报头域主要用于指定被请求资源的Internet主机和端口号,它通常从HTTP URL中提取出来的,eg:我们在浏览器中输入:http://www.baidu.com,此处使用缺省端口号80,若指定了端口号,则变成:Host:www.baidu.com:指定端口号 |
User-Agent |
User-Agent请求报头域允许客户端将它的操作系统、浏览器和其它属性告诉服务器。不过,这个报头域不是必需的,如果我们自己编写一个浏览器,不使用User-Agent请求报头域,那么服务器端就无法得知我们的信息了。 |
-
常见响应报头:
响应报头 |
描述 |
Allow |
指出服务器所支持的请求方式,如GET、POST 等方法。 |
Content-Encoding |
指定文档的编码方法。 |
Content-Length |
指定响应中数据的字节长度。 |
Content-Type |
指定回送数据的MIME类型。 |
Date |
指定发送HTTP消息的日期。 |
Last-Modified |
指定返回数据的最后修改时间。 |
Location |
重定向请求者到一个新的URI地址。 |
Refresh |
指定浏览器定时刷新的时间 |
Expires |
指定浏览器缓存数据的时间。 |
Server |
指定服务器名称,包含了处理请求的服务器使用的软件产品信息,与User-Agent请求报头相对应。 |
-
使用Fiddler抓取新浪首页请求响应报文(包含请求头):
-
JMeter中构成请求头的组件:
【cookie格式说明】
Cookie是由服务器端生成,发送给User-Agent(一般是浏览器),浏览器会将Cookie的key/value保存到某个目录下的文本文件内,下次请求同一网站时就发送该Cookie给服务器(前提是浏览器设置为启用cookie)。Cookie名称和值可以由服务器端开发自己定义,对于JSP而言也可以直接写入jsessionid,这样服务器可以知道该用户是否合法用户以及是否需要重新登录等。
Cookie在http协议中,仅仅是协议头(请求头或响应头)中的一个属性,只不过这个cookie属性的值很特别,又包含一组键值对集合。由于数据格式较长,所以在很多工具中,cookie被单独从协议头中剥离出来用表格进行单独显示。
下面是从Fiddler中随意摘取的一段有cookie信息的原始报文:
下面是JMeter中用于组装cookie的组件:
【状态码描述】
-
概要介绍
分类 |
分类描述 |
1** |
信息,服务器收到请求,需要请求者继续执行操作 |
2** |
成功,操作被成功接收并处理 |
3** |
重定向,需要进一步的操作以完成请求 |
4** |
客户端错误,请求包含语法错误或无法完成请求 |
5** |
服务器错误,服务器在处理请求的过程中发生了错误 |
-
详细说明
状态码 |
状态码英文名称 |
中文描述 |
【1开头的状态码】 |
||
100 |
Continue |
继续。客户端应继续其请求 |
101 |
Switching Protocols |
切换协议。服务器根据客户端的请求切换协议。只能切换到更高级的协议,例如,切换到HTTP的新版本协议 |
【2开头的状态码】 |
||
200 |
OK |
请求成功。一般用于GET与POST请求 |
201 |
Created |
已创建。成功请求并创建了新的资源 |
202 |
Accepted |
已接受。已经接受请求,但未处理完成 |
203 |
Non-Authoritative Information |
非授权信息。请求成功。但返回的meta信息不在原始的服务器,而是一个副本 |
204 |
No Content |
无内容。服务器成功处理,但未返回内容。在未更新网页的情况下,可确保浏览器继续显示当前文档 |
205 |
Reset Content |
重置内容。服务器处理成功,用户终端(例如:浏览器)应重置文档视图。可通过此返回码清除浏览器的表单域 |
206 |
Partial Content |
部分内容。服务器成功处理了部分GET请求 |
【3开头的状态码】 |
||
300 |
Multiple Choices |
多种选择。请求的资源可包括多个位置,相应可返回一个资源特征与地址的列表用于用户终端(例如:浏览器)选择 |
301 |
Moved Permanently |
永久移动。请求的资源已被永久的移动到新URI,返回信息会包括新的URI,浏览器会自动定向到新URI。今后任何新的请求都应使用新的URI代替 |
302 |
Found |
临时移动。与301类似。但资源只是临时被移动。客户端应继续使用原有URI |
303 |
See Other |
查看其它地址。与301类似。使用GET和POST请求查看 |
304 |
Not Modified |
未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源 |
305 |
Use Proxy |
使用代理。所请求的资源必须通过代理访问 |
306 |
Unused |
已经被废弃的HTTP状态码 |
307 |
Temporary Redirect |
临时重定向。与302类似。使用GET请求重定向 |
【4开头的状态码】 |
||
400 |
Bad Request |
客户端请求的语法错误,服务器无法理解 |
401 |
Unauthorized |
请求要求用户的身份认证 |
402 |
Payment Required |
保留,将来使用 |
403 |
Forbidden |
服务器理解请求客户端的请求,但是拒绝执行此请求 |
404 |
Not Found |
服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置"您所请求的资源无法找到"的个性页面 |
405 |
Method Not Allowed |
客户端请求中的方法被禁止 |
406 |
Not Acceptable |
服务器无法根据客户端请求的内容特性完成请求 |
407 |
Proxy Authentication Required |
请求要求代理的身份认证,与401类似,但请求者应当使用代理进行授权 |
408 |
Request Time-out |
服务器等待客户端发送的请求时间过长,超时 |
409 |
Conflict |
服务器完成客户端的PUT请求是可能返回此代码,服务器处理请求时发生了冲突 |
410 |
Gone |
客户端请求的资源已经不存在。410不同于404,如果资源以前有现在被永久删除了可使用410代码,网站设计人员可通过301代码指定资源的新位置 |
411 |
Length Required |
服务器无法处理客户端发送的不带Content-Length的请求信息 |
412 |
Precondition Failed |
客户端请求信息的先决条件错误 |
413 |
Request Entity Too Large |
由于请求的实体过大,服务器无法处理,因此拒绝请求。为防止客户端的连续请求,服务器可能会关闭连接。如果只是服务器暂时无法处理,则会包含一个Retry-After的响应信息 |
414 |
Request-URI Too Large |
请求的URI过长(URI通常为网址),服务器无法处理 |
415 |
Unsupported Media Type |
服务器无法处理请求附带的媒体格式 |
416 |
Requested range not satisfiable |
客户端请求的范围无效 |
417 |
Expectation Failed |
服务器无法满足Expect的请求头信息 |
【5开头的状态码】 |
||
500 |
Internal Server Error |
服务器内部错误,无法完成请求 |
501 |
Not Implemented |
服务器不支持请求的功能,无法完成请求 |
502 |
Bad Gateway |
充当网关或代理的服务器,从远端服务器接收到了一个无效的请求 |
503 |
Service Unavailable |
由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的Retry-After头信息中 |
504 |
Gateway Time-out |
充当网关或代理的服务器,未及时从远端服务器获取请求 |
505 |
HTTP Version not supported |
服务器不支持请求的HTTP协议的版本,无法完成处理 |
【总结】
下次当你看到不同的web工具时,是不是发现非常easy,工具无非就是把请求报文的3段格式,以及响应报文中的3段格式进行了分片可视化展现,然后实现了http(s)的请求功能,不同工具的差异性主要体现在展现形式、扩展功能、易用性等。所以在工作和学习的过程中,学会透过现象看本质,只有这样才能以不变应万变
做过功能测试的同学,如果你能理解界面和数据库表之间的关系,再回过头来想接口测试中的工具和协议之间的关系,是不是就恍然大悟了。对于不熟悉http同学来说,本篇信息量比较大,需要花点时间好好啃啃。如果你真正理解了本篇的含义,回忆下你曾使用过的http工具(如fiddler、jmeter、postman、charles、badboy等),是不是能准确的将界面元素和协议片段一一对应起来了,甚至那些使用java去调用httpclient包或者用python调用httplib包的同学,是不是能够将包中的对象、方法、属性,和协议中的每一个片段和属性对应起来。
如果觉得好,伸出你的指头转发吧。看完本篇后,有什么想法或者建议,请积极留言,我会抽空做相应解答。