4.2 服务器的相关知识
因为真正的后台应用是需要在服务器上面运行的,因此有必要在这里补充一下关于服务器的知识。
我们用到的大部分网络应用,如APP、Web网站、PC客户端甚至银行取款机的背后都提供了一个强大的后台服务,无论是电商、银行抑或游戏服务,对于一个用户来讲,我们感受到的仅仅是数据的传递和渲染,但是这些数据是怎么被汇总、计算或者存储的,大部分人无从得知,甚至感觉这一切是非常自然的事情。然则,事实并非如此。
目前,大部分网络应用都是搭建在分布式集群网络服务器上的,成千上万的服务器相互之间协调,联系,共享数据,构成了一个覆盖每一个用户的服务网络。
4.2.1 服务器概念
服务器就是一台高性能的计算机,它的高性能主要体现在高速度的运算能力、长时间的可靠运行、强大的外部数据吞吐能力等方面。服务器的构成与家用电脑基本相似,包括处理器、硬盘、内存、系统总线等,但它们是针对具体的网络应用特别制定的,因而服务器与微机在处理能力、稳定性、可靠性、安全性、可扩展性、可管理性等方面差异很大。
我们搭建的后台应用在生产环境下都是搭建在服务器上面的,因为服务器可以保证24小时不间断工作,而且能够保证很好地网络吞吐能力。而对于每一个用户来讲,在资源有限的情况的下,一个服务器的资源是被多个用户共享的,因此在硬件限制的情况下,就需要考虑软件的编写来优化用户的请求和处理,保证每个用户能够公平流畅的享受服务。
服务器长这个样子,里面安装的是Linux或WindowsServer系统哦
4.2.2 TCP/IP和HTTP协议
一个没有联网的单个服务器,除了能够当做一个大型的计算机之外,没有任何用途。只有把服务器之间联网,服务器和用户的电脑之间联网才能让用户和网络服务器提供商真正的建立联系。那么用户的浏览器和提供商的服务器的Web应用是怎么通过网络取得联系的呢?
为了解决这个问题,计算机网络分层体系和网络协议应运而生:
互联网协议按照功能不同分为OSI七层或TCP/IP五层或TCP/IP四层,搞清楚了每层的主要协议
就理解了整个互联网通信的原理。每层都运行特定的协议,越往上越靠近用户,越往下越靠近硬件。
什么是协议?
所谓协议就是互联网设备之间通信需要遵循的一套规则或者标准,消息的发送者和接收者都需要遵循这个标准才能理解对方表达的意思。
数据链路层:以太网协议属于数据链路层协议。
以太网协议规定:
一组信号构成一个数据包,叫做‘帧’
每一数据帧分成:报头head和数据data两部分
headdata
head包含:(固定18个字节)
发送者/源地址,6个字节
接收者/目标地址,6个字节
数据类型,6个字节
data包含:(最短46字节,最长1500字节)
数据包的具体内容
目标地址/源地址指代的就是mac地址,也就是我们电脑或通信设备的网卡地址,mac地址在全世界都是唯一的。
有了mac地址,同一网络内的两台主机就可以通信了,但以太网协议采用最原始的方式——广播的方式进行通信,即计算机通信基本靠吼。
网络层:IP协议属于网络层的协议。
如果所有的通信都采用以太网的广播方式,那么一台机器发送的包全世界都会收到,这简直难以想象。因此网络层就出现了。
世界上的大的网络由一个个小型的局域网组成,这样让以太网的包只能在局域网中传播可以避免随意乱吼,但是还必须保证某个以太网设备可以连接另外一个局域网的设备。因此,必须找出一种方法来区分哪些计算机属于同一广播域,哪些不是,如果是就采用广播的方式发送,如果不是,就采用路由的方式(向不同广播域/子网分发数据包)。
网络层功能:引入一套新的地址用来区分不同的广播域/子网,这套地址即网络地址。
IP协议:
规定网络地址的协议叫IP协议,它定义的地址称之为IP地址,广泛采用的v4版本即IPv4,它规定网络地址由32位2进制表示(现在IPv4早已经不够用啦,IPv6已经出来了)。
范围0.0.0.0-255.255.255.255
一个IP地址通常写成四段十进制数,例:172.16.10.1
ip地址分成两部分
网络部分:标识子网
主机部分:标识主机
但是注意:单纯的IP地址段只是标识了IP地址的种类,从网络部分或主机部分都无法辨识一个IP所处的子网,但是可以利用子网掩码来解决这个问题。
例:172.16.10.1与172.16.10.2并不能确定二者处于同一子网
所谓”子网掩码”,就是表示子网络特征的一个参数。它在形式上等同于IP地址,也是一个32位二进制数字,它的网络部分全部为1,主机部分全部为0。比如,IP地址172.16.10.1,如果已知网络部分是前24位,主机部分是后8位,那么子网络掩码就是11111111.11111111.11111111.00000000,写成十进制就是255.255.255.0。
知道”子网掩码”,我们就能判断,任意两个IP地址是否处在同一个子网络。方法是将两个IP地址与子网掩码分别进行AND运算(两个数位都为1,运算结果为1,否则为0),然后比较结果是否相同,如果是的话,就表明它们在同一个子网络中,否则就不是。
子网掩码的知识不需要深入了解,我们只需要知道:
IP协议的作用主要有两个,一个是为每一台计算机分配IP地址,另一个是确定哪些地址在同一个子网络。
IP数据包也分为head和data部分,无须为IP包定义单独的栏位,直接放入以太网包的data部分。
以太网 headIP headIP data
传输层:TCP协议属于传输层的协议。
以太网层的mac帮我们找到主机,网络层的IP帮我们区分子网,这样基本就能定位到是网络上的哪个设备了。但是大家在一个设备上使用了很多的应用程序,你的电脑上可能同时开启qq,chrome,穿越火线等多个应用程序,如何让通信直接联络到某个应用程序呢?答案就是端口,端口即应用程序与网卡关联的编号。
传输层功能:建立端口到端口的通信。端口范围0-65535,但0-1023为系统占用端口,不能给普通应用程序使用。
TCP协议:
TCP协议是可靠的传输,TCP数据包的长度一般不会超过IP数据包的长度,以确保单个TCP数据包不必再分割。
以太网 headIP headTCP headTCP data
UDP协议:
UDP协议是不可靠传输,”报头”部分一共只有8个字节,总长度不超过65,535字节,正好放进一个IP数据包。
以太网 headIP headUDP head(8Bytes)UDP data
TCP和UDP协议到底有何优劣
UDP提供简单的通信机制,传输过程中丢包或者包内容顺序错乱也无法检测重发。但是UDP协议的头部信息更少,可以承载更多的数据。同时能够避免TCP的三次握手,达到更高的传输效率,适用于DNS通信解析和音视频流媒体传输。
TCP相比UDP可以保证可靠的传输,能够检测包的完整性,同时它是有连接的协议(需要确保对方可以通信时才发送数据)。
TCP通过三次握手来建立连接(重点)
所谓TCP三次握手是指建立一个 TCP 连接时需要客户端和服务器端总共发送三个包以确认连接的建立。在socket编程中,这一过程由客户端执行connect来触发。
第一次握手:客户端将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给服务器端,客户端进入SYN_SENT状态,等待服务器端确认。
第二次握手:服务器端收到数据包后由标志位SYN=1知道客户端请求建立连接,服务器端将标志位SYN和ACK都置为1,ack=J+1,随机产生一个值seq=K,并将该数据包发送给客户端以确认连接请求,服务器端进入SYN_RCVD状态。
第三次握手:客户端收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给服务器端,服务器端检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,客户端和服务器端进入ESTABLISHED状态,完成三次握手,随后客户端与服务器端之间可以开始传输数据了。
TCP通过四次握手来终止连接(重点)
断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开。在socket编程中,这一过程由客户端或服务端任一方执行close来触发。
由于TCP连接是全双工的,因此,每个方向都必须要单独进行关闭,这一原则是当一方完成数据发送任务后,发送一个FIN来终止这一方向的连接,收到一个FIN只是意味着这一方向上没有数据流动了,即不会再收到数据了,但是在这个TCP连接上仍然能够发送数据,直到这一方向也发送了FIN。首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭。
中断连接端可以是客户端,也可以是服务器端。
第一次挥手:客户端发送一个FIN=M,用来关闭客户端到服务器端的数据传送,客户端进入FIN_WAIT_1状态。意思是说"我客户端没有数据要发给你了",但是如果你服务器端还有数据没有发送完成,则不必急着关闭连接,可以继续发送数据。
第二次挥手:服务器端收到FIN后,先发送ack=M+1,告诉客户端,你的请求我收到了,但是我还没准备好,请继续你等我的消息。这个时候客户端就进入FIN_WAIT_2 状态,继续等待服务器端的FIN报文。
第三次挥手:当服务器端确定数据已发送完成,则向客户端发送FIN=N报文,告诉客户端,好了,我这边数据发完了,准备好关闭连接了。服务器端进入LAST_ACK状态。
第四次挥手:客户端收到FIN=N报文后,就知道可以关闭连接了,但是他还是不相信网络,怕服务器端不知道要关闭,所以发送ack=N+1后进入TIME_WAIT状态,如果Server端没有收到ACK则可以重传。服务器端收到ACK后,就知道可以断开连接了。客户端等待了2MSL后依然没有收到回复,则证明服务器端已正常关闭,那好,我客户端也可以关闭连接了。最终完成了四次握手。
应用层协议:HTTP、HTTPS协议是应用层协议。
用户使用的应用程序均工作于应用层,但大家都可以开发自己的应用程序,而且数据多种多样,必须规定好数据的组织形式才能保证可以解析数据。
应用层功能:规定应用程序的数据格式。
HTTP协议也叫超文本传输协议,是一个基于请求与响应模式的、无状态的、应用层的协议。浏览器地址栏输入的URL,大部分都是基于HTTP或HTTPS协议的网址。
HTTP协议的组成
HTTP协议分请求和响应两部分规范:
请求:请求行(请求方法+URI+客户端协议版本)+消息报头+请求数据。
响应:状态行(服务器协议版本+状态码+状态码文本描述)+消息报头+响应数据。
HTTP是一个无状态的协议,无状态是指协议对于事务处理没有记忆能力,服务器不知道客户端是什么状态。即我们给服务器发送 HTTP 请求之后,服务器根据请求,会给我们发送数据过来,但是,发送完,不会记录任何信息。这意味着每个请求都是独立的,后续请求无法依赖上一次请求来判断身份,后续我们会学到如何解决HTTP无状态的问题。
1.0协议:
服务器需要处理同时面向全世界数十万、上百万客户端的网页访问,但每个客户端与服务器之间交换数据的间歇性较大(即传输具有突发性、瞬时性),并且网页浏览的联想性、发散性导致两次传送的数据关联性很低,大部分通道实际上会很空闲、无端占用资源。因此 HTTP 的设计者有意利用这种特点将协议设计为请求时建连接、请求完释放连接,以尽快将资源释放出来服务其他客户端。
2.0协议:
随着时间的推移,网页变得越来越复杂,里面可能嵌入了很多图片,这时候每次访问图片都需要建立一次 TCP 连接就显得很低效。后来,Keep-Alive 被提出用来解决这效率低的问题。
Keep-Alive 功能使客户端到服务器端的连接持续有效,当出现对服务器的后继请求时,Keep-Alive 功能避免了建立或者重新建立连接。
HTTPS协议
HTTPS协议是在HTTPS协议的基础上对报文进行加密的协议,因此HTTPS协议更安全。
HTTPS协议需要到CA申请证书。CA(数字证书认证,Certificate Authority)机构,是承担公钥合法性检验的第三方权威机构,负责指定政策、步骤来验证用户的身份,并对SSL证书进行签名,确保证书持有者的身份和公钥的所有权。
SSL证书可加密敏感信息使其不被泄露,还可以提供身份验证,防止钓鱼网站,SSL证书是由受信任的CA机构颁发的,申请证书时会严格的验证企业/组织的信息,可以消除浏览器“不安全”提示。最后SSL证书还可以提高SEO关键词排名,谷歌、百度等浏览器,明确表示对安装SSL证书的网站进行优先暂时排名的权利。
SSL证书的颁发和HTTPS访问流程:
用户产生自己的密钥对,把公钥和身份信息发送给CA认证中心。
CA核实之后把数字证书发送给用户,该证书包含用户的个人信息和公钥,附带认证中心的私钥签名。
用户把认证证书放在网站下,通过HTTPS提供服务。
浏览器内置了各大权威CA机构根证书,也就包含了CA的公钥,和用户网站进行公钥私钥比对就能建立HTTPS/SSL连接。
4.2.3 浏览器请求接收流程
打开Chrome浏览器,地址栏输入了网址:https://www.google.com的执行流程:
通过DNS进行域名解析,我们访问的是一个域名https://www.google.com,但是网络设备只能通过ip地址才能找到对方的主机。因此首先需要域名解析:
Chrome先从自身DNS缓存中找,但浏览器只能缓存1000条。
如果浏览器找不到,会查看操作系统的DNS缓存。
如果操作系统也没有找到,则会根据本机配置的首选DNS服务器发起域名解析请求(UDP协议的DNS 53端口)。
DNS运营商首先从自身查找解析,找不到就迭代的去世界根DNS服务器查找(世界有13台根DNS服务器)。
最后找到了域名对应的IP地址,则开始下一步动作。
浏览器根据IP,遵循网络协议找到对方主机,开始建立TCP的3次握手。
浏览器会以随机端口向服务器发送TCP请求,这中间要经过各种网络设备,甚至还要经过层层防火墙。
建立TCP连接后开始发送HTTP请求,服务器响应HTTP请求,生成HTML页面代码作为响应体数据发送给浏览器。
Chrome浏览器利用Webkit内核解析HTML和CSS ,利用V8内核解析JavaScript,请附带请求其他资源。
浏览器解析的结果会生成一个DOM树和CSS规则树,组合构造成一个渲染树。
渲染树并不等同于 DOM 树,渲染树只会包括需要显示的节点和这些节点的样式信息。
CSS 规则树 主要是为了完成匹配并把 CSS规则 附加到渲染树上的每个 元素上面。
当浏览器生成渲染树以后,就会根据渲染树来进行布局(也可以叫做回流)。这一阶段浏览器要做的事情是要弄清楚各个节点在页面中的确切位置和大小。通常这一行为也被称为“自动重排”。
最后通过调用操作系统 Native GUI 的 API 绘制。
4.2.4 API接口和RESTFul规范
1. 耦合架构和前后端分离
在Vue和React框架出现之前,前端页面的开发都是由后端开发人员来完成,大部分页面都是通过后端的模板渲染引擎(例如JSP、PHP、Django)来完成的,直到目前为止,依然有很多网站采用这样的开发方式。
这种开发方式是采用耦合的架构,所谓耦合,也就是前后端开发没有分离,而是把数据和UI拼凑在了一起。采用这种方式的开发具有以下缺点:
后端开发人员既要负责前端还要负责后端
如果数据结构变化,需要修改模板以适应数据变化
不专业的开发人员很容易编写大量页面造成项目无法维护
页面的js文件编写随意,依赖重复加载或忽略加载
因此,随着技术的演进,前端和后端逐渐分离,形成了当前大前端的趋势。前后端的数据交互,完全通过ajax来实现,传输数据约定的规则就是采用RESTFul API。
采用这种方式开发可以让前端开发人员只负责交互和渲染,后端开发人员只负责数据维护和生成,能够有效进行分工,这种开发模式一般称为前后端分离。
2. API接口
API接口(Application Programming Interface,应用程序接口)本身就是HTTP(S)协议的一部分,它约定客户端和服务器数据传输格式的形式。
下面就是一个API接口,但是仅仅只定义了一个地址,却没有提供功能说明和其他部分。
https://www.npmjs.com/search?q=express
当前的软件开发领域,API接口的定义规范主流的规则被称为RESTFul(Representational State Transfer,表述性状态转移)规范,了解了RESTFul规范就能熟悉整个API接口的规范了。
3. RESTFul规范
RESTFul规范大体遵循以下几点:
标准接口URL写法:协议://域名[:端口]/版本/路径?过滤查询,URL结尾不应该包含斜杠“/”,路径中首选小写字母,路径名词均为复数,端口号如果没有输入,表示80的默认端口。
如:
查询分页数据,https://api.itcast.com/v1/books?page=0&size=10
查询单条数据:https://api.itcast.com/v1/books/id
对接口的操作,如获取、新建、更新、删除,需要在接口方法中指定,而不是在路径中提供动词。
如:GET https://api.itcast.com/v1/books?pageNum=0&pageSize=10
状态码:服务器返回的信息,成功与否必须通过状态码来确定,而不是放到response的body内部。如果状态码错误,那么response的body中需要包含一个error字段来展示错误信息。
返回的结果:
GET返回数组或单个资源对象。
POST返回新生成的对象。
PUT、PATCH返回修改的对象
DELETE返回空对象。
4. 请求和响应的Header
Header是HTTP的消息头部,位于HTTP消息报头部分。虽然它不在RESTFul规范之内,但是它里面的信息非常重要,Header是由一堆键值对组成的配置信息。由于HTTP协议分为请求和响应两部分,对于每个动作,Header里面的配置会有一些区别。
Header也可以添加自定义键值对,需要键名以X-开头,如X-Access-Token:xxx,一般默认的浏览器定义的Header无法手动覆盖和修改。
常用的HTTP请求Header
协议头说明示例状态
Accept可接受的响应内容类型(Content-Types)。Accept: text/plain固定
Accept-Charset可接受的字符集Accept-Charset: utf-8固定
Accept-Encoding可接受的响应内容的编码方式。Accept-Encoding: gzip, deflate固定
Accept-Language可接受的响应内容语言列表。Accept-Language: en-US固定
Accept-Datetime可接受的按照时间来表示的响应内容版本Accept-Datetime: Sat, 26 Dec 2015 17:30:00 GMT临时
Authorization用于表示HTTP协议中需要认证资源的认证信息Authorization: Basic OSdjJGRpbjpvcGVuIANlc2SdDE==固定
Cache-Control用来指定当前的请求/回复中的,是否使用缓存机制。Cache-Control: no-cache固定
Connection客户端(浏览器)想要优先使用的连接类型Connection: keep-alive固定
Cookie由之前服务器通过Set-Cookie(见下文)设置的一个HTTP协议CookieCookie: $Version=1; Skin=new;固定:标准
Content-Length以8进制表示的请求体的长度Content-Length: 348固定
Content-MD5请求体的内容的二进制 MD5 散列值(数字签名),以 Base64 编码的结果Content-MD5: oD8dH2sgSW50ZWdyaIEd9D==废弃
Content-Type请求体的MIME类型 (用于POST和PUT请求中)Content-Type: application/x-www-form-urlencoded固定
Date发送该消息的日期和时间(以RFC 7231中定义的"HTTP日期"格式来发送)Date: Dec, 26 Dec 2015 17:30:00 GMT固定
Expect表示客户端要求服务器做出特定的行为Expect: 100-continue固定
From发起此请求的用户的邮件地址From: user@itbilu.com固定
Host表示服务器的域名以及服务器所监听的端口号。如果所请求的端口是对应的服务的标准端口(80),则端口号可以省略。Host: www.itbilu.com:8080固定
If-Match仅当客户端提供的实体与服务器上对应的实体相匹配时,才进行对应的操作。主要用于像 PUT 这样的方法中,仅当从用户上次更新某个资源后,该资源未被修改的情况下,才更新该资源。If-Match: "9jd00cdj34pss9ejqiw39d82f20d0ikd"固定
If-Modified-Since允许在对应的资源未被修改的情况下返回304未修改If-Modified-Since: Dec, 26 Dec 2015 17:30:00 GMT固定
If-None-Match允许在对应的内容未被修改的情况下返回304未修改( 304 Not Modified ),参考 超文本传输协议 的实体标记If-None-Match: "9jd00cdj34pss9ejqiw39d82f20d0ikd"固定
If-Range如果该实体未被修改过,则向返回所缺少的那一个或多个部分。否则,返回整个新的实体If-Range: "9jd00cdj34pss9ejqiw39d82f20d0ikd"固定
If-Unmodified-Since仅当该实体自某个特定时间以来未被修改的情况下,才发送回应。If-Unmodified-Since: Dec, 26 Dec 2015 17:30:00 GMT固定
Max-Forwards限制该消息可被代理及网关转发的次数。Max-Forwards: 10固定
Origin发起一个针对跨域资源共享的请求(该请求要求服务器在响应中加入一个Access-Control-Allow-Origin的消息头,表示访问控制所允许的来源)。Origin: http://www.itbilu.com固定: 标准
Pragma与具体的实现相关,这些字段可能在请求/回应链中的任何时候产生。Pragma: no-cache固定
Proxy-Authorization用于向代理进行认证的认证信息。Proxy-Authorization: Basic IOoDZRgDOi0vcGVuIHNlNidJi2==固定
Range表示请求某个实体的一部分,字节偏移以0开始。Range: bytes=500-999固定
Referer表示浏览器所访问的前一个页面,可以认为是之前访问页面的链接将浏览器带到了当前页面。Referer其实是Referrer这个单词,但RFC制作标准时给拼错了,后来也就将错就错使用Referer了。Referer: http://itbilu.com/nodejs固定
TE浏览器预期接受的传输时的编码方式:可使用回应协议头Transfer-Encoding中的值(还可以使用"trailers"表示数据传输时的分块方式)用来表示浏览器希望在最后一个大小为0的块之后还接收到一些额外的字段。TE: trailers,deflate固定
User-Agent浏览器的身份标识字符串User-Agent: Mozilla/……固定
Upgrade要求服务器升级到一个高版本协议。Upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11固定
Via告诉服务器,这个请求是由哪些代理发出的。Via: 1.0 fred, 1.1 itbilu.com.com (Apache/1.1)固定
Warning一个一般性的警告,表示在实体内容体中可能存在错误。Warning: 199 Miscellaneous warning固定
常用的HTTP响应头
响应头说明示例状态
Access-Control-Allow-Origin指定哪些网站可以跨域源资源共享Access-Control-Allow-Origin: *临时
Accept-Patch指定服务器所支持的文档补丁格式Accept-Patch: text/example;charset=utf-8固定
Accept-Ranges服务器所支持的内容范围Accept-Ranges: bytes固定
Age响应对象在代理缓存中存在的时间,以秒为单位Age: 12固定
Allow对于特定资源的有效动作;Allow: GET, HEAD固定
Cache-Control通知从服务器到客户端内的所有缓存机制,表示它们是否可以缓存这个对象及缓存有效时间。其单位为秒Cache-Control: max-age=3600固定
Connection针对该连接所预期的选项Connection: close固定
Content-Disposition对已知MIME类型资源的描述,浏览器可以根据这个响应头决定是对返回资源的动作,如:将其下载或是打开。Content-Disposition: attachment; filename="fname.ext"固定
Content-Encoding响应资源所使用的编码类型。Content-Encoding: gzip固定
Content-Language响就内容所使用的语言Content-Language: zh-cn固定
Content-Length响应消息体的长度,用8进制字节表示Content-Length: 348固定
Content-Location所返回的数据的一个候选位置Content-Location: /index.htm固定
Content-MD5响应内容的二进制 MD5 散列值,以 Base64 方式编码Content-MD5: IDK0iSsgSW50ZWd0DiJUi==已淘汰
Content-Range如果是响应部分消息,表示属于完整消息的哪个部分Content-Range: bytes 21010-47021/47022固定
Content-Type当前内容的MIME类型Content-Type: text/html; charset=utf-8固定
Date此条消息被发送时的日期和时间(以RFC 7231中定义的"HTTP日期"格式来表示)Date: Tue, 15 Nov 1994 08:12:31 GMT固定
ETag对于某个资源的某个特定版本的一个标识符,通常是一个 消息散列ETag: "737060cd8c284d8af7ad3082f209582d"固定
Expires指定一个日期/时间,超过该时间则认为此回应已经过期Expires: Thu, 01 Dec 1994 16:00:00 GMT固定: 标准
Last-Modified所请求的对象的最后修改日期(按照 RFC 7231 中定义的“超文本传输协议日期”格式来表示)Last-Modified: Dec, 26 Dec 2015 17:30:00 GMT固定
Link用来表示与另一个资源之间的类型关系,此类型关系是在RFC 5988中定义Link:; rel="alternate"固定
Location用于在进行重定向,或在创建了某个新资源时使用。Location: http://www.itbilu.com/nodejs固定
P3PP3P策略相关设置P3P: CP="This is not a P3P policy!固定
Pragma与具体的实现相关,这些响应头可能在请求/回应链中的不同时候产生不同的效果Pragma: no-cache固定
Proxy-Authenticate要求在访问代理时提供身份认证信息。Proxy-Authenticate: Basic固定
Public-Key-Pins用于防止中间攻击,声明网站认证中传输层安全协议的证书散列值Public-Key-Pins: max-age=2592000; pin-sha256="……";固定
Refresh用于重定向,或者当一个新的资源被创建时。默认会在5秒后刷新重定向。Refresh: 5; url=http://itbilu.com
Retry-After如果某个实体临时不可用,那么此协议头用于告知客户端稍后重试。其值可以是一个特定的时间段(以秒为单位)或一个超文本传输协议日期。示例1:Retry-After: 120示例2: Retry-After: Dec, 26 Dec 2015 17:30:00 GMT固定
Server服务器的名称Server: nginx/1.6.3固定
Set-Cookie设置HTTP cookieSet-Cookie: UserID=itbilu; Max-Age=3600; Version=1固定: 标准
Status通用网关接口的响应头字段,用来说明当前HTTP连接的响应状态。Status: 200 OK
TrailerTrailer用户说明传输中分块编码的编码信息Trailer: Max-Forwards固定
Transfer-Encoding用表示实体传输给用户的编码形式。包括:chunked、compress、 deflate、gzip、identity。Transfer-Encoding: chunked固定
Upgrade要求客户端升级到另一个高版本协议。Upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11固定
Vary告知下游的代理服务器,应当如何对以后的请求协议头进行匹配,以决定是否可使用已缓存的响应内容而不是重新从原服务器请求新的内容。Vary: *固定
Via告知代理服务器的客户端,当前响应是通过什么途径发送的。Via: 1.0 fred, 1.1 itbilu.com (nginx/1.6.3)固定
Warning一般性警告,告知在实体内容体中可能存在错误。Warning: 199 Miscellaneous warning固定
WWW-Authenticate表示在请求获取这个实体时应当使用的认证模式。WWW-Authenticate: Basic固定
Header中的Content-Type
Content-Type表示请求或响应的数据类型。
其格式是type/subType;charset;boundary
type:subType:主类型/次类型,如text/html,application/json。
charset,字符编码标准,一般是utf-8。
boundary,用于分隔请求主体的字段分隔符。
常用的用于请求的有如下几个
application/x-www-form-urlencoded: 数据被编码成以 '&' 分隔的键-值对, 同时以 '=' 分隔键和值. 非字母或数字的字符会被转码,不要用于文件等二进制数据。在POST中会放到body中,在GET中会追加到URL后面。express的body-parser通过urlencoded方法来解析。
application/x-www-form-urlencoded请求的主体内容
POST / HTTP/1.1
Content-Type:application/x-www-form-urlencoded//指定了类型
Accept-Encoding: gzip, deflate
Host: w.sohu.com
Content-Length: 21//content内容长度
Connection: Keep-Alive
Cache-Control: no-cache// 下面是一条空行隔开的内容,然后是被格式化的请求参数。
txt1=hello&txt2=world
multipart/form-data:在application/x-www-form-urlencoded的基础上支持文件上传,但body中的内容会更多,所以如果没有文件,不建议用这个。
boundary用来告诉服务器从哪里分隔主体内容。
multipart/form-data请求的主体内容
POST /t2/upload.do HTTP/1.1
User-Agent: SOHUWapRebot
Accept-Language: zh-cn,zh;q=0.5
Accept-Charset: GBK,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Content-Length: 60408
// boundary用来告诉服务器从哪里分隔主体内容
// multipart/form-data的数据主体很明显是把每一个字段都做了细致的描述,如每个字段还可以包含Content-Disposition,Content-Type,Content-Transfer-Encoding属性描述,来告诉服务器如何解析这个字段,因此会显著增大传输的内容容量。
Content-Type:multipart/form-data; boundary=ZnGpDtePMx0KrHh_G0X99Yef9r8JZsRJSXC
Host: w.sohu.com
--ZnGpDtePMx0KrHh_G0X99Yef9r8JZsRJSXC
Content-Disposition: form-data; name="city"
Santa colo
--ZnGpDtePMx0KrHh_G0X99Yef9r8JZsRJSXC
Content-Disposition: form-data;name="desc"
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
...
--ZnGpDtePMx0KrHh_G0X99Yef9r8JZsRJSXC
Content-Disposition: form-data;name="pic"; filename="photo.jpg"
Content-Type: application/octet-stream
Content-Transfer-Encoding: binary
... binary data of the jpg ...
--ZnGpDtePMx0KrHh_G0X99Yef9r8JZsRJSXC--
application/json:支持序列化的json(JSON.stringify),序列化的json可以存储更复杂的键值对嵌套结构,包括数组、Date等的原生转换。
POST http://www.example.com HTTP/1.1
Content-Type: application/json;charset=utf-8
{"title":"test","sub":[1,2,3]}
text/plain:传递的数据就是纯文本,服务器不做任何处理。
text/html:传递的数据会被解析成html。
5. 接口测试工具
Postman
Postman是一个非常好用的API调试工具,通过这个工具可以任意指定和查看HTTP请求的Header(包括Content-Type)、方法以及请求数据,而且能够对数据进行格式化。在测试服务器接口的时候,这个工具能够让我们快速的预览和调试接口。
Chrome -> 开发者工具 -> NetWork
除了使用第三方工具,浏览器一般在开发者工具中也集成了接口调试,在其中可以查看接口请求的所有信息。
Swagger
Swagger是一个API接口文档生成工具,除了能够根据annotation生成API文档之外,自动生成的文档中还可以测试每个接口。
安装
npm install --save swagger-ui-express
编写一个符合swagger规范的API文档,规范格式参考swagger editor。命名为swagger.json,放到工程根目录下。
{
"swagger": "2.0",
"info": {
"description": "这是一个测试的API文档,你可以使用**Markdown语法**",
"version": "1.0.0",
"title": "API文档",
"contact": {
"email": "apiteam@swagger.io"
}
},
"host": "localhost:3000",
"basePath": "/v1",
"tags": [
{
"name": "user",
"description": "用户操作",
"externalDocs": {
"description": "更多信息请见",
"url": "http://swagger.io"
}
}
],
"schemes": [
"https",
"http"
],
"paths": {
"/user": {
"post": {
"tags": [
"user"
],
"summary": "添加用户",
"description": "",
"operationId": "addPet",
"consumes": [
"application/json",
"application/xml"
],
"produces": [
"application/xml",
"application/json"
],
"parameters": [
{
"in": "body",
"name": "name",
"description": "用户的名字",
"required": true
}
],
"responses": {
"405": {
"description": "非法输入"
}
}
}
}
}
}
修改index.js代码
const express = require('express')
const swaggerUi = require('swagger-ui-express')
const app = express()
const swaggerDocument = require('./swagger.json');
// 访问api地址可以查看api
app.use('/api', swaggerUi.serve, swaggerUi.setup(swaggerDocument))
app.listen(3000, () => {
console.log('服务器创建完毕,监听3000端口')
})
执行index.js
node index.js
浏览器打开http://localhost:3000/api 即可查看效果。
点击user一栏,会展开,点击Try it out即可测试接口数据。
4.3 Express常用中间件
了解了服务器和客户端的通信原理,接下来我们就可以使用Express来编写自己Web应用的接口了。Express编写业务逻辑大部分都是基于中间件去实现的,中间件封装了一些特定的逻辑,辅助我们可以快速的开发。
4.3.1 什么是中间件
Express 是一个路由和中间件 Web 框架,其自身只具有最低程度的功能:Express 应用程序基本上是一系列中间件函数调用。
Express 应用程序可以使用以下类型的中间件: