一、HTTP
http
是我们前后台交互的时候的传输协议(即超文本传输协议)
1.HTTP 的工作流程
- 和服务器建立链接
- 建立链接后,发送一个请求给服务器(请求)
- 服务器接受到请求以后进行相应的处理并给出一个回应(响应)
- 断开于服务器的链接
1.1 和服务器建立链接
-
怎么和服务器建立链接呢?
-
需要保证客户端的接受和发送正常,服务器端的接受和发送正常
-
这里就涉及到一个东西叫做
TCP/IP
协议 -
建立链接的主要步骤叫做
三次握手
-
客户端发送一个消息给到服务端
此时: 服务端知道了 客户端可以正常发送消息 服务端知道了 服务端可以正常接受消息
-
服务端回给客户端一个消息
此时: 服务端知道了 客户端可以正常发送消息 服务端知道了 服务端可以正常接受消息 客户端知道了 客户端可以正常发送消息 客户端知道了 客户端可以正常接受消息 客户端知道了 服务端可以正常接受消息 客户端知道了 服务端可以正常发送消息
-
客户端再回给服务端一个消息
此时: 服务端知道了 客户端可以正常发送消息 服务端知道了 服务端可以正常接受消息 客户端知道了 客户端可以正常发送消息 客户端知道了 客户端可以正常接受消息 客户端知道了 服务端可以正常接受消息 客户端知道了 服务端可以正常发送消息 服务端知道了 服务端可以正常发送消息 服务端知道了 客户端可以正常接受消息
-
-
至此,依照
TCP/IP
协议的建立链接就建立好了 -
双方都知道双方可以正常收发消息
-
就可以进入到第二步,通讯了
1.2 发送一个请求
-
建立完链接以后就是发送请求的过程
-
我们的每一个请求都要把我们的所有信息都包含请求
-
每一个请求都会有一个
请求报文
-
在
请求报文
中会包含我们所有的请求信息(也就是我们要和服务端说的话都在里面) -
我们的请求报文中会包含几个东西
-
请求行
POST /user HTTP/1.1 # POST 请求方式 # /user 请求URL(不包含域名) # HTTP/1.1 请求协议版本
-
请求头(请求头都是键值对的形式出现的)
user-agent: Mozilla/5.0 # 产生请求的浏览器信息 accept: application/json # 表示客户端希望接受的数据类型 Content-Type: application/x-www-form-urlencoded # 客户端发送的实体数据格式 Host: 127.0.0.1 # 请求的主机名(IP)
-
请求空行(请求头和请求主体之间要留一个空白行)
# 就是一个空行
-
请求体(本次请求携带的数据)
# GET 请求是没有请求体数据的 # POST 请求才有请求体数据
-
-
接下来看一个完整的请求报文
POST /user HTTP/1.1 # 请求行 Host: www.user.com Content-Type: application/x-www-form-urlencoded accept: application/json User-agent: Mozilla/5.0. # 以上是首部 #(此处必须有一空行) # 空行分割header和请求内容 name=world # 请求体
1.3 接受一个响应
-
客户端的请求发送到服务端以后
-
服务端进行对应的处理
-
会给我们返回一个响应
-
每一个响应都会有一个
响应报文
-
在
响应报文
中会包含我们所有的响应信息(也就是服务端在接受到客户端请求以后,给我们的回信) -
我们的
响应报文
中会包含几个信息-
状态行
HTTP/1.1 200 OK # HTTP/1.1 服务器使用的 HTTP 协议版本 # 200 响应状态码 # OK 对响应状态码的简单解释
-
响应头
Date: Jan, 14 Aug 2019 12:42:30 GMT # 服务器时间 Server: Apache/2.4.23 (Win32) OpenSSL/1.0.2j PHP/5.4.45 # 服务器类型 Content-Type: text/html # 服务端给客户端的数据类型 Content-Length: 11 # 服务端给客户端的数据长度
-
响应体
hello world # 服务端给客户端的响应数据
-
1.4 断开于服务端的链接
- 之前我们的建立链接是基于
TCP/IP
协议的三次握手
- 我们的断开链接是基于
TCP/IP
协议的四次挥手
- 客户端发送一个我要断开的消息给服务端
- 服务端接受到以后发送一个消息告诉客户端我已经进入关闭等待状态
- 服务端再次发送一个消息告诉客户端,这个是我的最后一次消息给你,当我再接受到消息的时候就会关闭
- 客户端接受到服务端的消息以后,告诉服务器,我已经关闭,这个是给你的最后一个消息
1.5 完成一个 HTTP 请求
- 至此,一个 HTTP 请求就完整的完成了
- 一个 HTTP 请求必须要包含的四个步骤就是
- 建立链接
- 发送请求
- 接受响应
- 断开链接
- 在一个 HTTP 请求中,请求的部分有请求报文,接受响应的部分有响应报文
- 请求报文包含
- 请求行
- 请求头
- 请求空行
- 请求体
- 响应报文
- 状态行
- 响应头
- 响应体
2.常见的 HTTP 响应状态码
- 在一个 HTTP 请求的响应报文中的状态行会有一个响应状态码
- 这个状态码是用来描述本次响应的状态的
- 通常会出现五种状态码
- 100 ~ 199
- 200 ~ 299
- 300 ~ 399
- 400 ~ 499
- 500 ~ 599
100 ~ 199 请求继续
-
一般我们看不到,因为表示请求继续
-
100: 继续请求,前面的一部分内容服务端已经接受到了,正在等待后续内容
-
101: 请求者已经准备切换协议,服务器页表示同意
200 ~ 299 各种成功
- 2 开头的都是表示成功,本次请求成功了,只不过不一样的状态码有不一样的含义(语义化)
- 200: 标准请求成功(一般表示服务端提供的是网页)
- 201: 创建成功(一般是注册的时候,表示新用户信息已经添加到数据库)
- 203: 表示服务器已经成功处理了请求,但是返回的信息可能来自另一源
- 204: 服务端已经成功处理了请求,但是没有任何数据返回
300 ~ 399 重定向
- 3 开头也是成功的一种,但是一般表示重定向
- 301: 永久重定向
- 302: 临时重定向
- 304: 使用的是缓存的数据
- 305: 使用代理
400 ~ 499 各种客户端错误
- 4 开头表示客户端出现错误了
- 400: 请求的语法服务端不认识
- 401: 未授权(你要登录的网站需要授权登录)
- 403: 服务器拒绝了你的请求
- 404: 服务器找不到你请求的 URL
- 407: 你的代理没有授权
- 408: 请求超时
- 410: 你请求的数据已经被服务端永久删除
500 ~ 599 各种服务端错误
- 5 开头的表示服务端出现了错误
- 500: 服务器内部错误
- 503: 服务器当前不可用(过载或者维护)
- 505: 请求的协议服务器不支持
3.常见的 HTTP 请求方式
- 每一个 HTTP 请求在请求行里面会有一个东西叫做请求方式
- 不同的请求方式代表的含义不同
- GET: 一般用于获取一些信息使用(获取列表)
- POST: 一般用于发送一些数据给服务端(登录)
- PUT: 一般用于发送一些数据给服务当让其添加新数据(注册)
- DELETE: 一般用域删除某些数据
- HEAD: 类似于 GET 的请求,只不过一般没有响应的具体内容,用于获取报文头
- CONNECT: HTTP/1.1 中预留的方式,一般用于管道链接改变为代理的时候使用
- PATCH: 是和 PUT 方式类似的一个方式,一般用于更新局部数据
- OPTIONS: 允许客户端查看服务端性能
- 我们比较常用的就是 GET 和 POST
3.1 GET
请求
- 参数以
querystring
的形式发送,也就是直接拼接在 请求路径的后面 - GET 请求会被浏览器主动缓存
- GET 请求根据不同的浏览器对长度是有限制的
- IE: 2083 个字符
- FireFox: 65536 个字符
- Safari: 80000 个字符
- Opera: 190000 个字符
- Chrome: 8182 个字符
- APACHE(server): 理论上接受的最大长度是 8192 个字符(有待商榷)
- 对参数的类型有限制,只接受 ASCII 码的格式
- GET 请求是明文发送,相对不安全
3.2 POST
请求
- 参数以
request body
的形式发送,也就是放在请求体中 - POST 请求不会被浏览器主动缓存,除非手动设置
- POST 请求理论上是没有限制的,除非服务端做了限制
- 对参数类型没有限制,理论上可以传递任意数据类型,只不过要和请求头对应
- POST 请求是密文发送,相对安全
二、cookie
字符串存储数据
cookie
是一个以字符串的形式存储数据的位置- 每一个 HTTP 请求都会在请求头中携带 cookie 到服务端
- 每一个 HTTP 响应都会在响应头中携带 cookie 到客户端
- 也就是说,cookie 是不需要我们手动设置,就会自动在 客户端 和 服务端之间游走的数据
- 我们只是需要设置一下 cookie 的内容就可以
1.cookie
的存储形式
-
cookie 是以字符串的形式存储,在字符串中以
key=value
的形式出现 -
每一个
key=value
是一条数据 -
多个数据之间以
;
分割// cookie 的形态 'a=100; b=200; c=300;'
2.cookie
的特点
- 存储大小有限制,一般是 4 KB 左右
- 数量有限制,一般是 50 条左右
- 有时效性,也就是有过期时间,一般是 会话级别(也就是浏览器关闭就过期了)
- 有域名限制,也就是说谁设置的谁才能读取
3.前端操作cookie
3.1document.cookie
设置
-
不带过期时间的语法: document.cookie = “key=value”
- 一次设置一条,是不带有过期时间的
- 如果想设置过期时间,那么需要一些修饰信息
-
带过期时间的语法: document.cookie = “key=value;expires=时间对象”
- 不管你给他一个什么时间点的时间对象
- cookie都会当作世界标准时间来使用了
- 因为我们的时间是中国标准时间
- 所以我们设置的时间对象要提前8个小时
// 设置一个时效性为会话级别的 cookie document.cookie = 'a=100' // 设置一个有过期时间的 cookie document.cookie = 'b=200;expires=Thu, 18 Dec 2043 12:00:00 GMT";' // 上面这个 cookie 数据会在 2043 年 12 月 18 日 12 点以后过期,过期后会自动消失
3.2document.cookie
修改
- 和设置cookie是一摸一样的
- 只不过重新设置一个已经存在的cookie就可以了
3.3document.cookie
删除
- 删除cookie就是重新设置一下这个cookie
- 把过期时间设置成当前时间之前就可以了
// 因为 cookie 不能直接删除
// 所以我们只能把某一条 cookie 的过期时间设置成当前时间之前
// 那么浏览器就会自动删除 cookie
document.cookie = 'b=200;expires=Thu, 18 Dec 2018 12:00:00 GMT";'
3.4document.cookie
获取
- 直接使用document.cookie
- 如果里面只有一条cookie
- 字符串"a=100"
- 如果里面有两条cookie
- 字符串"a=100; b=200"
- 如果里面没有信息
- 空字符串 “”
const cookie = document.cookie
console.log(cookie) // 就能得到当前 cookie 的值
总结
- 只有一个操作cookie的办法:document.cookie
- 浏览器里面,控制台 -> application(应用)-> cookies
- 是浏览器对象cookie空间的优化
- 为了让我们看起来更方便一些
- 上面有一些按钮
- 叉子按钮:删除一条cookie
- 圈圈斜线按钮:清除所有cookie
- 刷新按钮:刷新这个空间的显示信息
4.cookie
操作封装
- 因为 js 中没有专门操作
cookie
增删改查的方法 - 所以需要我们自己封装一个方法
4.1 设置cookie
/**
* setCookie 用于设置 cookie
* @param {STRING} key 要设置的 cookie 名称
* @param {STRING} value 要设置的 cookie 内容
* @param {NUMBER} expires 过期时间
*/
function setCookie (key, value, expires) {
const time = new Date()
time.setTime(time.getTime() - 1000 * 60 * 60 * 24 * 8 + expires) // 用于设置过期时间
document.cookie = `${key}=${value};expires=${time};`
}
4.2 读取cookie
/**
* getCookie 获取 cookie 中的某一个属性
* @param {STRING} key 你要查询的 cookie 属性
* @return {STRING} 你要查询的那个 cookie 属性的值
*/
function getCookie(key) {
const cookieArr = document.cookie.split(';')
let value = ''
cookieArr.forEach(item => {
if (item.split('=')[0] === key) {
value = item.split('=')[1]
}
})
return value
}
4.3 删除cookie
/**
* delCookie 删除 cookie 中的某一个属性
* @param {STRING} name 你要删除的某一个 cookie 属性的名称
*/
function delCookie(name) {
setCookie(name, 1, -1)
}
三、同源策略
- 同源策略是由浏览器给的
- 浏览器不允许我们向别人发送请求,只能向自己的服务器发送请求
- 当我们想向别人的服务器发送请求的时候,就会被浏览器阻止了
- 什么是 “别人的服务器” 呢?
- 当 请求协议/域名/端口号 有任意一个不同的时候,那么就算是别人的服务器
- 这个时候就会触发同源策略
- 我们管触发了 同源策略 的请求叫做跨域请求
四、跨域解决方案
- 有的时候我们是需要实现跨域请求的
- 我们需要多个服务器给一个页面提供数据
- 那么这个时候我们就要想办法解决跨域问题
1.jsonp
利用script的src
jsonp
是我们实现跨域请求的手段,是把我们之前的东西组合在一起使用的技术手段而已- 利用的是 script 标签来实现
script
标签的本质
-
过程一:
-
浏览器给我们提供了一个
script
标签 -
它的本质就是请求一个外部资源,是不受到同源策略的影响的
-
同时
script
标签的src
属性,也是一种请求,也能被服务器接收到 -
并且:
- script标签的src属性请求回来的东西是一个字符串,浏览器会把这个字符串当作 js 代码来执行
-
所以我们就可以利用这个
script
标签的src
属性来进行跨域请求了
-
-
过程二:
- 如果返回的内容就是一个“__jp1(‘hello world’)“
- script标签会把这个字符串当作js代码来执行
- 就相当于于执行了一句__jp1(‘hello world’)
- 我当前页面没有__jp1这个函数,这个代码会来的时候就会报错了
- 报错是因为没有__jp1这个函数
- 解决:只要在请求这个服务器地址之前
- 自己提前定义好一个__jp1函数
总结:
- jsonp解决方案的思路是
- 在script标签的src请求这个地址之前
- 提前定义好一个被调用的函数
- 通过script标签的src请求一个服务器地址
- 这个服务器地址如果没有特殊说明
- 有一个参数callback后面的值
- 可以指定你希望后端调用的函数名
- 这个服务器地址返回的内容是调用提前定义好的函数
- 只能是get请求
2.cors
引入外部插件
- 这个方案主要是由后端操作
- 前端和以前一样还是用ajax对象发请求,但是不会有跨域问题
- cors原理:
- 跨域是浏览器不让ajax对象请求别人家的服务器
- 如果别人家的服务器告诉浏览器,我允许这个域名请求我
- 浏览器就管不着了,就可以正常请求了
服务器设置允许跨域的响应头就可以了
-
如何设置
-
方案1:
-
打开 其他资料/server/src/app.js 文件
-
把第7行(const cors = require(‘cors’))和第10行注释(app.use(cors()))
-
在第10行的后面书写允许跨域的响应头
app.use(function(req,res,next){ // res的setHeader可以设置响应头 // * 通配的意思,所有域名都可以 res.setHeader('Access-Control-Allow-Origin','*');// 允许哪些域名请求我 res.setHeader('Access-Control-Request-Methods','GET,POST,PUT,DELETE,OPTIONS');// 允许哪些请求方式 res.setHeader('Access-Control-Allow-Headers','x-requested-with,content-type,test-token,test-sessid');// 允许携带哪些请求头信息 // 上面三行代码一写,那么就等于这个服务器开启了跨域资源共享 // 就可以跨域请求了 // 调用next函数,继续后面的内容 next() })
-
重新启动服务器就可以了
-
-
方案2:
- 打开 其他资料/server/src/app.js 文件
- 在app.js的第7行写:const cors = require(‘cors’)
- 就是引入外部的一个插件,这个插件是专门用来设置允许跨域请求的响应头的
- 在app.js的第10使用这个插件:app.use(cors())
-
3. 配置代理
3.1 proxy
代理跨域
- 因为“浏览器不允许”你请求一个“非同源”的地址
- 解决问题:
- 在浏览器同源设置一个代理服务器
- 把本该发送给目标服务器的请求发送给代理服务器
- 由代理服务器 转发请求给 目标服务器
- 目标服务器把 响应返回给 代理服务器
- 代理服务器 再把响应给到 浏览器
怎么进行代理设置
- 发出请求的服务器: http://127.0.0.1:5500
- 代理服务器:http://127.0.0.1:5500
- 这个服务器是vscode的插件写的,改不了
- 代理服务器:http://localhost:8888
- 这个服务器允许跨域,我可以请求他,就能要求他给我代理
- 凡事我请求http://localhost:8888服务器,然后地址里面有/api几个字的
- 都帮我转发到https://mm.cxbiao.cn/服务器
- 就需要在http://localhost:8888设置代理
- 目标服务器: https://mm.cxbiao.cn
设置代理的步骤
-
打开 其他资料/server/src/app.js
-
app.js第7行(const cors = require(‘cors’))和第10行(app.use(cors()))不能注释
-
下载代理请求的第三方包(插件)
- 插件的名称是: http-proxy-middleware
- server/src 位置打开命令行
- 输入: npm i http-proxy-middleware
- 回车,开始下载
- 重新出现光标就下载完成了(中间如果有报红色的err就是下载出错了)
-
继续在app.js文件的第7行的后面书写代码,引入这个插件
- const {createProxyMiddlewaare} = require(‘http-proxy-middleware’)
-
继续在app.js文件的第10行的后面书写插件配置
app.use( '/api', // 凡是你请求当前8888服务器的地址中包含/api的,就进行转发 createProxyMiddleware({ target:'https://mm.cxbiao.cn' , // target后面是要请求要转发到的目标服务 changeOrigin:true // 设置这个目标域名是否是跨域的 }) )
-
设置完成以后要重启服务器
3.2 正反向代理
- 代理,分成两种,正向代理和反向代理
正向代理
- 有一个客户端需要向一个非同源的服务器B发送请求
- 我们搭建一个和客户端同源的服务器A
- 当客户端发送请求的时候,由服务器A来接受
- 再由服务器A向服务器B发送请求,因为 同源策略是由浏览器给的,服务器之间没有
- 服务器B接受到请求以后,会处理请求,并把响应返回给服务器A
- 再由服务器A把响应给到客户端就可以了
- 我们就可以用这个方式来进行跨域请求了
反向代理
- 反向代理一般是用来做负载均衡的
- 当我请求一个服务器的时候,其实请求的是服务器端设置的代理服务器
- 由代理服务器把若干大量的请求分发给不同的服务器进行处理
- 再由服务器把响应给到代理服务器
- 代理服务器返回给客户端