前端工具 | AJAX (Axios)

99 阅读7分钟

在 js 中向服务器发送请求来加载数据

方案:XMLHTTPRequest(xhr)、Fetch、Axios(现在最常用)

Rest 接口

传统服务器的局限

MVC:Model数据模型,View视图,呈现数据,Controller控制器,调度,加载数据并选择视图来呈现

传统服务器直接给客户端返回一个 HTML 页面,但是这无法适应现在的应用场景——一个应用通常有多个客户端存在(如 pc/移动端)。 那么除 web 外的其他类型的客户端是不是还要单独开发服务器来适配呢?

解决方法

传统服务器需要加载数据+将数据模型渲染进视图+返回视图,于是我们将渲染视图的功能从服务器剥离,服务器只负责向客户端返回数据,渲染视图的工作由客户端自行完成,于是服务器就可以同时为多种客户端提供服务,也减轻了服务器的压力。

Rest

一种服务器的设计风格(服务器只返回数据)

通常使用 json 作为数据格式

GET加载数据
POST添加数据
PUT添加/修改数据
PATCH修改数据
DELETE删除数据
OPTION浏览器自动发送,检查请求的权限

CORS 跨域资源共享

例如 http://localhost:5000http://127.0.0.1:5000

协议,域名,端口号三个只要有一个不同,就算跨域。

当我们通过AJAX去发送跨域请求时,浏览器为了服务器的安全,会阻止JS读取到服务器的数据。

解决方案: 在服务器中设置一个允许跨域的头
Access-Control-Allow-Origin:允许哪些客户端访问我们的服务器

Cross-Origin Resource Sharing (CORS) - HTTP | MDN (mozilla.org)

例如在 node 中设置所有客户端均可访问:

res.setHeader("Access-Control-Allow-Origin","*")

允许某客户端可以访问:

res.setHeader("Access-Control-Allow-Origin","http://127.0.0.1:5000")

localStorage

本地存储用户信息并进行前端验证。

为什么不用 cookie 和 sessionStorsge?

cookie 是服务器创建并发给客户端保存。session 是基于 cookie,但是 rest 服务器是跨域的,限制对 Cookie 和 Session 的访问,需要额外的配置才能使用,很麻烦。

为什么跨域请求中限制对 Cookie 和 Session 的访问?

Cookie 和 Session 是用于在客户端和服务器之间维持会话状态的机制,涉及到敏感信息的传递。而跨域请求可能存在安全风险,为了防止恶意网站窃取用户的敏感信息,浏览器实现了同源策略,默认情况下,跨域请求不会携带 Cookie 和 Session 信息。

如果需要使用,则要在浏览器和服务器中做一些额外的配置。

所以要使用 localStorage 将用户数据存储到浏览器内部。

// setItem() 用来存储数据
// getItem() 用来获取数据
// removeItem() 删除数据
// clear() 清空数据

token 后端验证

客户端可以通过 localStorage 验证用户的登录状态,但是rest 服务器是无状态服务器,仅将用户信息发给客户端而不存储用户的数据。所以服务器要如何得知用户的登录状态呢?—— 使用token。

什么是 token

  • 访问资源接口(API)时所需要的资源凭证。其作用是在服务端验证用户身份,防止恶意网站伪造用户的身份和用户请求执行非法操作。
  • token 是一个由服务器通过加密包生成的随机值,它会嵌入到每个表单或 AJAX 请求中。前端通过将该令牌包含在请求中,以确保该请求是合法的。后端在接收到请求时会验证令牌的有效性,如果令牌不匹配或不存在,则拒绝该请求。
  • 基于 token 的用户认证是一种服务端无状态的认证方式,服务端不用存放 token 数据,从而减轻服务器的压力,减少频繁的查询数据库。

前后端通信中 token 怎么用

  1. 客户端使用用户名跟密码请求登录
  2. 服务端收到请求,去验证用户名与密码
  3. 验证成功后,服务端会签发一个 token 并把这个 token 发送给客户端
  4. 客户端收到 token 以后,会把它存储起来,比如放在 cookie 里或者 localStorage 里
  5. 客户端每次向服务端请求资源的时候需要带着服务端签发的 token
  6. 服务端收到请求,然后去验证客户端请求里面带着的 token ,如果验证成功,就向客户端返回请求的数据

image.png

注意

  • 每一次请求都需要携带 token,需要把 token 放到 HTTP 的 Header 里。
  • token是在服务器端进行颁发和验证的,而不是在浏览器中进行处理的,所以它可以避开同源策略(跨域问题)。

token 怎么生成

在 node 中可以使用 jsonwebtoken 来对 json 进行加密,生成一个 web 中使用的令牌。

// 引入jsonwebtoken
const jwt = require("jsonwebtoken")

// 要加密的数据
const obj = {...}

//加密:需要加密的json,自定义密匙,加密配置对象)
//注:密匙不能泄露
const token = jwt.sign(obj, "XXX", {
  expiresIn: "1"  //生成的token的有效时间
})

//......然后服务器会把token发给客户端,
//客户端再度发来请求时就需要带上token以告诉服务器自己现在是合法请求
//---------------------------------------------------------------------

//服务器验证token
try {
  //服务器收到客户端的token后
  //解密:需要解密的token,和加密时相同的密匙
  const decodeData = jwt.verify(token, "hellohellohowyou")
  console.log(decodeData)
} catch (e) {
  // 说明token解码失败,说明token无效
  console.log("无效的token")
}

token 使用案例

1、客户端使用用户名和密码请求登录。

2、服务器验证用户名和密码并生成 token 返回给客户端。

3、客户端接收 token 存进 localStorge。

4、 客户端请求数据,请求头里携带之前保存的 token。

(增加了 token 的请求头,所以同样要在服务器设置该头允许跨域,不然前端请求就发不过去了。)

5、服务器验证 token,验证成功则返回资源。

Axios

Axios中文文档 | Axios中文网 (axios-http.cn)

和 fetch 的区别

  • 在 data 里写一个 对象,它就会自动把请求头格式设置为 json;写一个 urlencoded 格式的数据,它就自动把请求头设置为urlencoded;
  • axios 将服务器返回的数据封装成 "result",里面存了很多信息,result.data 也已经自动帮我们转成 js 对象了;
  • axios 只有在返回状态码为 2XX 的时候才会执行.then,无需再自己判断状态码。

常用配置


  axios({
      baseURL:"http://localhost:3000",// 服务器的根目录(路径前缀)
      url:"test", // 请求地址 
      method:"get", //请求方法,默认是get
      // 请求头(可不写,它会根据data自动识别)
      // headers:{"Content-type":"application/json"}
      // 请求体
      // data:"name=zxx&age=18"  //urlencoded格式
      data: {  //json格式
          name: "zxx",
          address: "ZheJiang"
      },
      // 指定路径中的查询字符串
      params:{
          id:1,
          name:"swk"
      },

      //timeout 过期时间,1s后没有响应结果自动取消请求
      timeout:1000,

      // 终止请求
      // signal

      // transformRequest 用来对请求data做一系列处理
      // 一个数组作为参数,数组里有多个函数
      // 请求发送时多个函数会按序执行,下一函数的参数即上一个函数修改后的结果
      // 注意,上一函数需要return data才能使下一函数的参数data有值
      // 注意,用了这玩意影响axios对请求头的自动识别,若headers["Content-Type"]设置得不对(如写成content-type),会使得请求一直是默认的urlencoded
      transformRequest:[function(data, headers){
          // 可以在函数中对data和headers进行修改
          data.name = "猪八戒"
          headers["Content-Type"] = "application/json"
          return data
      }, function(data, headers){
          return JSON.stringify(data)
        // 最后一个函数必须返回一个字符串,才能使得数据有效
      }]
      
  })
      .then((result) => { //状态码2XX才执行
          console.log(result.data)  // result是axios封装过
      })
      .catch((err) => {
          console.log("出错了!", err)
      })
}

响应结构

响应结构 | Axios中文文档 | Axios中文网 (axios-http.cn)

默认配置

指定默认配置,这些配置对所有 axios 请求生效,就不必在每一个 axios 请求里重复写了。

axios.defaults.baseURL = "http://localhost:3000"
axios.defaults.headers.common[
    "Authorization"
] = `Bearer ${localStorage.getItem("token")}`

axios 实例

axios 实例相当于是 axios 的一个副本,功能一模一样。

axios 的默认配置在实例里也同样会生效。

但是我可以单独修改 axios 实例的默认配置,而该实例默认配置也只对该实例有效。

//创建实例并设置配置
const instance1 = axios.create({
    baseURL:"http://localhost:1000"
})
//创建实例并设置默认配置
const instance2 = axios.create()
instance.defaults.baseURL = "http://localhost:2000"

document.getElementById("btn1").onclick = () => {
  instance1  //baseURL是instance1的默认配置 http://localhost:1000
    .get("students")
    .then((res) => console.log(res.data))
    .catch((err) => {
      console.log("出错了", err)
    })
}

axios 拦截器

在请求发送前和响应读取前处理数据——拦截请求或者响应。

可以请求发送前,在拦截器里对请求的配置对象进行修改,常见做法是加上 token 权限信息。

拦截器只对当前实例有效。instance1.interceptors.request.use(...) 只对 instance1 生效。

axios.interceptors.request.use(
   // 在发送请求之前做些什么
  function (config)   // config 表示axios中的配置对象
      // console.log("拦截器执行了")
      config.headers["Authorization"] = `Bearer ${localStorage.getItem("token")}`
      return config
  },
    // 对请求错误做些什么
  function (error) {
      return Promise.reject(error)
  }
)
// 添加响应拦截器
axios.interceptors.response.use(function (response) {
    // 2xx 范围内的状态码都会触发该函数
    // 对响应数据做点什么
    return response;
  }, function (error) {
    // 超出 2xx 范围的状态码都会触发该函数
    // 对响应错误做点什么
    return Promise.reject(error);
  });

清除拦截器

const instance = axios.create();
//清除单个拦截器
const myInterceptor = instance.interceptors.request.use(function () {/*...*/});
axios.interceptors.request.eject(myInterceptor);
//清除所有请求拦截器
instance.interceptors.request.clear(); // Removes interceptors from requests
//清除所有响应拦截器
instance.interceptors.response.use(function () {/*...*/});
instance.interceptors.response.clear(); // Removes interceptors from responses

参考

www.bilibili.com/video/BV1Gg…

傻傻分不清之 Cookie、Session、Token、JWT - 掘金 (juejin.cn)