1. HTTP 的报文结构
对于 TCP 而言,在传输的时候分为两个部分:TCP头和数据部分。
而 HTTP 类似,也是header + body的结构,大致为如下内容:
起始行 + 头部 + 空行 + 实体
http起始行
对于请求报文来说,起始行类似下面这样:
GET /home HTTP/1.1 (方法 / 路径 协议/版本)
在起始行中,每两个部分之间用空格隔开,最后一个部分后面应该接一个换行,严格遵循ABNF语法规范。
对于响应报文来说,起始行如下:
HTTP/1.1 200 OK (协议 / 版本 状态码 状态描述)
对于http的头部字段格式,可以简单概括为以下几点:
- 字段名字段名不区分大小写
- 字段名不允许出现空格,不可以出现下划线
_ - 字段名后面必须紧接着
:
空行
用来分隔头部和**实体;**如果在头部中间故意加一个空行的话,那么空行后的内容全部被视为实体。
实体
特指具体的数据,也就是body部分。请求报文对应请求体, 响应报文对应响应体。
2. 请你说一下HTTP有哪些请求方法?
http/1.1规定了以下请求方法(都需要大写):
- GET: 通常用来获取资源
- HEAD: 获取资源的元信息
- POST: 提交数据,即上传数据
- PUT: 修改数据
- DELETE: 删除资源(几乎用不到)
- CONNECT: 建立连接隧道,用于代理服务器
- OPTIONS: 列出可对资源实行的请求方法,用来跨域请求
- TRACE: 追踪请求-响应的传输路径
请你讲一讲GET和POST的区别?
- 首先回答要看是基于什么前提的,如果什么前提都没有,不使用任何规范,只考虑语法和理论上的HTTP协议,那GET和POST几乎没有什么区别,只有名字不一样,仅此而已。
- 如果是基于RFC规范的话可以概况为以下几点:
-
理论上的:GET和POST是具有相同语法的,但是有不同的语义。GET是用来获取数据的,POST是用来发送数据的,其他方面没有区别。
-
实现上的: 浏览器就是这个规范的实现者。所以才有常见的那些不同,可以分为如下:
-
GET的数据在URL是可见的。POST 请求是不显示在URL中的。 -
GET请求对长度是有限制的,POST 的长度是无限制的。 -
GET请求的数据可以收藏为书签,POST 请求到的数据不可收藏为书签。 -
GET请求后,按后退按钮、刷新按钮是无影响的,但是如果是POST 的话,数据就会被重新提交。 -
GET只能进行 URL 编码,只能接收 ASCII 字符,而 POST 没有限制。 -
GET的历史参数会被保留在浏览器里,POST是不会保存在浏览器里的 -
GET与POST 相比,GET安全性较差。因为所发的数据是url的一部分。 -
GET 请求会把请求报文一次性发出去,而 POST 会分为两个 TCP 数据包,首先发 header 部分,如果服务器响应 100(continue), 然后发 body 部分。
3. 讲一讲你知道的HTTP状态码吧
RFC 规定 HTTP 的状态码为三位数,被分为五类:
- 1xx: 表示目前是协议处理的中间状态,还需要后续操作。
- 2xx: 表示成功状态。
- 3xx: 重定向状态,资源位置发生变动,需要重新请求。
- 4xx: 请求报文有误。
- 5xx: 服务器端发生错误。
常见的1xx状态码如下:
101 Switching Protocols。在HTTP升级为WebSocket的时候,如果服务器同意变更,就会发送状态码 101。
常见的2xx状态码如下:
200 OK是见得最多的成功状态码。通常在响应体中放有数据。
204 No Content含义与 200 相同,但响应头后没有 body 数据。
206 Partial Content顾名思义,表示部分内容,它的使用场景为 HTTP 分块下载和断点续传,当然也会带上相应的响应头字段Content-Range。
常见的3xx状态码如下:
301 Moved Permanently即永久重定向,对应着302 Found,即临时重定向。
304 Not Modified: 当协商缓存命中时会返回这个状态码。
常见的4xx状态码如下:
403 Forbidden: 服务器禁止访问,原因可能是法律禁止、信息敏感等等问题。
404 Not Found: 资源未找到,表示没在服务器上找到相应的资源。
405 Method Not Allowed: 请求方法不被服务器端允许。
406 Not Acceptable: 资源无法满足客户端的条件。
408 Request Timeout: 服务器等待了太长时间。
409 Conflict: 多个请求发生了冲突。
413 Request Entity Too Large: 请求体的数据过大。
414 Request-URI Too Long: 请求行里的 URI 太大。
429 Too Many Request: 客户端发送的请求过多。
431 Request Header Fields Too Large请求头的字段内容太大。
常见的5xx状态码如下:
500 Internal Server Error: 仅仅告诉你服务器出错了,出了啥错咱也不知道。
501 Not Implemented: 表示客户端请求的功能还不支持。
502 Bad Gateway: 服务器自身是正常的,但访问的时候出错了,啥错误咱也不知道。
503 Service Unavailable: 表示服务器当前很忙,暂时无法响应服务。
5. 可不可以讲一下HTTP有哪些特点?
HTTP特点如下:
-
灵活可扩展,主要体现在两个方面。一个是语义上的自由,只规定了基本格式,比如空格分隔单词,换行分隔字段,其他的各个部分都没有严格的语法限制。另一个是传输形式的多样性,不仅仅可以传输文本,还能传输图片、视频等任意数据,非常方便。
-
可靠传输。HTTP 基于 TCP/IP协议,因此把这一特性继承了下来。
-
请求-应答。可以概况为一发一收,有来有回。这个请求方和应答方不单单指客户端和服务器之间,如果某台服务器作为代理来连接后端的服务端,那么这台服务器也会扮演请求方的角色。
-
无状态。每次 http 请求都是独立、无关的,默认不需要保留通信过程的上下文状态信息。
6. 那你再说说HTTP有哪些缺点?
无状态
在需要长连接的场景中,需要保存大量的上下文信息,以免传输大量重复的信息,这时候无状态就是 http 的缺点。
但反过来看,可能有另外一些应用仅仅只是为了获取一些数据,不需要保存连接上下文信息,无状态反而减少了网络开销,成为了 http 的优点。
明文传输
HTTP协议里的报文头部不使用二进制数据,而是文本形式。
这对于调试提供了便利,但同时也让 HTTP 的报文信息暴露给了外界,给攻击者提供了便利。常见的WIFI陷阱就是利用 HTTP 明文传输的缺点,诱导用户连上热点,然后疯狂抓走所有的流量,从而拿到被害者的敏感信息。
队头阻塞问题
当 HTTP 开启长连接时,共用一个 TCP 连接,同一时刻只能处理一个请求,那么当前请求耗时过长的情况下,其它的请求只能处于阻塞状态,也就是著名的队头阻塞问题。后面也有很多方法解决了这个问题,下一个帖子再解释如何解决的。
7. 你知道跨域吗?讲讲
在前后端分离的开发模式中,经常会遇到跨域问题,即 Ajax 请求发出去了,服务器也成功响应了,前端就是拿不到这个响应。
那什么是跨域?
浏览器遵循同源政策(scheme(协议)、host(主机)和port(端口)都相同则为同源。非同源站点有这样一些限制:
- 不能读取和修改对方的 DOM
- 不读访问对方的 Cookie、IndexDB 和 LocalStorage
- 限制 XMLHttpRequest 请求。
当浏览器向目标 URI 发 Ajax 请求时,只要当前 URL 和目标 URL 不同源,则产生跨域,被称为跨域请求。
跨域请求的响应一般会被浏览器所拦截,但归根结底其实是被浏览器拦截,响应其实是成功到达客户端了。
8. 那你可不可以说说如何解决跨域呢?
1. CORS
CORS 其实是 W3C 的一个标准,全称是跨域资源共享。它需要浏览器和服务器的共同支持。下面先说两个概念,分别是: 简单请求和非简单请求。
浏览器根据请求方法和请求头的特定字段,将请求做了一下分类,具体来说规则是这样,凡是满足下面条件的属于简单请求:
- 请求方法为 GET、POST 或者 HEAD
- 请求头的取值范围: Accept、Accept-Language、Content-Language、Content-Type(只限于三个值
application/x-www-form-urlencoded、multipart/form-data、text/plain)
在以上范围里面的就是简单请求, 范围外的就是非简单请求,然后针对这两种不同的请求进行不同的处理。
而对于两种请求的详细剖析这里不做展开,可以参考如下博客:
而CORS 简单可以概括为浏览器自动加上**Origin**字段,服务端响应头返回Access-Control-Allow-Origin。
2. JSONP
虽然XMLHttpRequest对象遵循同源政策,但是script标签不一样,它可以通过 src 填上目标地址从而发出 GET 请求,实现跨域请求并拿到响应。这也就是 JSONP 的原理。
值得一提的是,面试官有可能会让你封装一个JSONP,下面是JSONP的实现源码,可做参考:
function JSONP({
url,
params,
callbackKey,
callback
}) {
// 在参数⾥制定 callback 的名字
params = params || {}
params[callbackKey] = 'jsonpCallback'
// 预留 callback
window.jsonpCallback = callback
// 拼接参数字符串
const paramKeys = Object.keys(params)
const paramString = paramKeys
.map(key => `${key}=${params[key]}`)
.join('&')
// 插⼊ DOM 元素
const script = document.createElement('script')
script.setAttribute('src', `${url}?${paramString}`)
document.body.appendChild(script)
}
JSONP({
url: 'xxxx.com',
params: {
key: 'test',
},
callbackKey: '_cb',
callback(result) {
console.log(result.data)
}
})
和CORS相比,JSONP 最大的优势在于兼容性好,IE 低版本不能使用 CORS 但可以使用 JSONP,缺点也很明显,请求方法单一,只支持 GET 请求。
3. Nginx
Nginx 是一种高性能的反向代理服务器,可以用来轻松解决跨域问题。
反向代理可以理解为拿到客户端的请求,将请求转发给其他的服务器,主要的场景是维持服务器集群的负载均衡,换句话说,反向代理帮其它的服务器拿到请求,然后选择一个合适的服务器,将请求转交给它。
4. postMessage
postMessage()⽅法允许来⾃不同源的脚本采⽤异步⽅式进⾏有限的通信, 可以实现跨⽂本档、多窗⼝、跨域消息传递。
5. WebSocket
WebSocket 是⼀种双向通信协议,在建⽴连接之后,WebSocket 的 server 与 client 都能主动向对⽅发送或接收数 据,连接建⽴好了之后 client 与 server 之间的双向通信就与 HTTP ⽆关了,因此可以跨域。
6. window.name + iframe
window.name属性值在不同的⻚⾯(甚⾄不同域名)加载后依旧存在,并且可以⽀持⾮常 ⻓的 name 值,我们可以利⽤这个特点进⾏跨域。
7. location.hash + iframe
比如a.html欲与c.html跨域相互通信,通过中间⻚b.html来实现。 三个⻚⾯,不同域之间利⽤ iframe的location.hash传值,相同域之间直接js访问来通信。
8. document.domain + iframe
该⽅式只能⽤于⼆级域名相同的情况下,⽐如 a.test.com 和 b.test.com 适⽤于该⽅ 式,我们只需要给⻚⾯添加 document.domain ='test.com' 表示⼆级域名都相同就可以实现跨域,两个⻚⾯都通过js 强制设置document.domain为基础主域,就实现了同域。