编程崽

登录

一叶在编程苦海沉沦的扁舟之上,我是那只激情自射的崽

Axios 简单使用

Axios 简单使用

Axios 支持浏览器和服务端进行 ajax 请求。

Axios中文文档

基础使用

基本的 Axios 使用,和 jquery 差不多。

安装:

sh 复制代码
npm install -S axios

以下几个语法,都能发出请求,且返回值为一个 Promise:

js 复制代码
import Axios from 'axios'

(async () => {
  let res = await Axios(url[, config])
  let res = await Axios.request(config)
  let res = await Axios.get(url[, config])
  let res = await Axios.delete(url[, config])
  let res = await Axios.head(url[, config])
  let res = await Axios.options(url[, config])
  let res = await Axios.post(url[, data[, config]])
  let res = await Axios.put(url[, data[, config]])
  let res = await Axios.patch(url[, data[, config]])
})()

且可以为 Axios 添加一些全局的默认配置,一次执行,即使在其他文件中再使用 Axios,也同样生效:

js 复制代码
import Axios from 'axios'
// 配置默认配置
Axios.defaults.baseURL = 'http://localhost:1213'
Axios.defaults.headers = {'content-type': 'application/json;charset=utf-8'}

(async () => {
  let res = await Axios('/getNameGet')
})()

后面这个 config 的配置项很多,常用的添加参数、添加消息体等常用选项,都在其中配置:

js 复制代码
const config = {
  baseURL: 'http://localhost:1213',
  method: 'post', // 默认 get 请求
  headers: {'a-b': 'XMLHttpRequest'},
  url: '/getNamePost',
  responseType: 'json', // 响应的格式
  // 请求体数据
  data: {
    firstName: 'Fred'
  },
  // 即将与请求一起发送的,以 ? 和 & 拼接在 URL 中的参数
  params: {
    a: 100
  },

  // timeout: 1000 * 60, // 超时,毫秒


  // 请求前拦截,只能用在 'PUT', 'POST' 和 'PATCH' 这几个请求方法
  // 必须返回一个字符串,或 ArrayBuffer,或 Stream
  transformRequest: [function (data, headers) {
    // 对 data 进行任意转换处理
    return data;
  }],

  // 响应后拦截
  transformResponse: [function (data) {
    // 对 data 进行任意转换处理
    return data;
  }],

  // 负责 `params` 序列化的函数
  // paramsSerializer: function(params) {
  //   return Qs.stringify(params, {arrayFormat: 'brackets'})
  // },


  // 表示跨域请求时是否需要使用凭证...发送请求时,是否带 cookie
  // withCredentials: false, // default

  // `adapter` 允许自定义处理请求,以使测试更轻松
  // 返回一个 promise 并应用一个有效的响应 (查阅 [response docs](#response-api)).
  // adapter: function (config) {
  //   /* ... */
  // },

  // 服务器响应的数据类型,可以是 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream'

  // 响应的编码
  // responseEncoding: 'utf8', // default

  // 定义允许的响应内容的最大长度
  // maxContentLength: 2000,

  // 'proxy' 定义代理服务器的主机名称和端口
  // `auth` 表示 HTTP 基础验证应当用于连接代理,并提供凭据
  // 这将会设置一个 `Proxy-Authorization` 头,覆写掉已有的通过使用 `header` 设置的自定义 `Proxy-Authorization` 头。
  // proxy: {
  //   host: '127.0.0.1',
  //   port: 9000,
  //   auth: {
  //     username: 'mikeymike',
  //     password: 'rapunz3l'
  //   }
  // },

  // `cancelToken` 指定用于取消请求的 cancel token
  // cancelToken: new CancelToken(function (cancel) {
  // })
}

创建服务+请求拦截

可以全局创建一个 Axios 服务,对这个服务初始化一些统一的配置和统一的拦截。

创建和配置服务

如代码示例,分三个部分:

  • Axios.create:创建请求服务
  • service.interceptors.request.use:请求前的拦截
  • service.interceptors.response.use:响应后的拦截

request.js 中:

js 复制代码
// import Cookies from 'js-cookie';
import Axios from 'axios';

// 创建一个 axios 服务
const service = Axios.create({
  // 默认配置们
  baseURL: 'http://localhost:1213',
  timeout: 1000 * 60, // 超时时间, 毫秒
  // withCredentials: true, // 发送请求时,是否带 cookie
  headers: {
    'Content-Type': 'application/json; charset=utf-8'
  }
})

/**
 * 请求拦截
 */
service.interceptors.request.use(config => {
  // 自动添加请求头
  // if (Cookies.get('token')) {
  //   config.headers['Authorization'] = `Bearer ${Cookies.get('token')}`
  // }

  // 对请求体的参数,进行处理
  // if (config.method.toUpperCase() === 'GET') { // get 请求的参数在 params 字段中
  //   config.params = config.params || {}
  //   config.params.type = 'get'
  // }
  // if (config.method.toUpperCase() === 'POST') { // post 请求的参数在 data 字段中
  //   config.data = config.data || {}
  //   config.data.type = 'get'
  // }

  return config;
}, Promise.reject)

/**
 * 响应拦截
 * 发起请求时,可在 url、method、data 的同级传入参数,用来配置得到响应后的处理
 */
service.interceptors.response.use(async response => {
  return await getResponse(response)
}, async err => {
  // 打印错误
  console.error(err)
  
  if (err.response) return await getResponse(err.response)

  // 超时
  if (err?.message?.includes('timeout')) {
    Message.error('服务器连接失败') // 请求超时
  } else if (err?.message?.includes('Network Error')) { // 网络错误
    Message.error('网络连接失败') // 网络错误
  } else {
    Message.error('服务出错,请稍后再试!')
  }
  return [false, null, err]
})

// 返回结果的处理程序
async function getResponse(response) {
  const {
    handle = true, // 进行参数的 code 和其他处理?
    tip = true, // 接口返回 code 非正常时,自动弹出错误提示?
    // reloadFun = null, // 重新调用该接口时,需要掉这个方法拿到新的配置(主要是 token 放在参数里了,需要重新获取)
  } = response.config || {}

  let success = null, data = null

  // 进行数据预处理再返回给调用者
  if (handle) {
    const allData = response.data
    if (!allData) {
      success = false
      console.error('后台没有返回数据', response)
      if (tip) message.error('无数据响应')
    } else {
      success = allData.code === 0
      if (success) {
        data = allData.data
      } else {
        // success 为 false 的处理
        data = allData

        // if (response.data && response.data.code === 401) { // 401, token失效
        //   clearLoginInfo() // 清除用户token
        //   router.push({ name: 'login' }) // 其他操作
        // }

        // 未登录或 token 过期
        // if ([11013, 11014, 11015, 11016].includes(allData.code)) {
        //   // 刷新 token
        //   let [isSuccess] = await store.dispatch('auth/refreshToken')
        //   // 刷新 token 成功,直接再去调刚才这个接口
        //   if (isSuccess) return reloadFun ? reloadFun() : $axios(response.config)
        //   else await store.dispatch('auth/logout', false)
        // }

        if (tip) { // 需要弹框提醒
          if (allData.msg && typeof(allData.msg) === 'string') Message.error(allData.msg)
          else Message.error('接口服务出错')
        }
      }
    }
  }
  return [success, data, response]
}
export default service;

携带Cookie相关配置说明

默认情况,网站与接口同域,则调用接口时自带携带 cookie,如果网站与接口不是同一域名,则不携带。

但当非同一域名时,仍然想携带 cookie,需把配置项 withCredentials 设置为 true,同时后台的响应头需要做一些处理:

  • Access-Control-Allow-Credentials 值要为 true
  • Access-Control-Allow-Origin 和 Access-Control-Allow-Headers 值不能为 *,需要代码具体指定。

以下为 node 的服务端设置响应头的代码:

js 复制代码
response.setHeader('Access-Control-Allow-Origin', request.headers.origin) // 允许跨域的请求头
response.setHeader('Access-Control-Allow-Headers', 'content-type,all,h-common,h-post,h-get')
response.setHeader('Access-Control-Allow-Credentials', 'true')

使用创建的服务定义接口们

api.js 文件中:

js 复制代码
// 引入上面那个服务
import service from './request'

export const getNameGet = (data) => (
  // get 获取 name
  service({
    url: '/getNameGet',
    method: 'get',
    params: data,
  })
);

export const getNamePost = (data) => (
  // post 获取 name
  service({
    url: '/getNamePost',
    method: 'post',
    data,
  })
);

其他页面使用接口

某个页面:

js 复制代码
// 引入接口
import { getNameGet, getNamePost } from './api'

// 在 async 异步函数中使用(或者使用 Promise,不再距离)
(async function() {

  try {
    // 使用 await,这里是请求成功,也就是 Axios 服务中,没有使用 Promise.reject 返回的结果
    let result = await getNameGet({a: 10, b: 20})
    console.log(result)
  } catch (error) {
    // Axios 服务中,使用 Promise.reject 返回的结果,走这里
    console.log(error)
  }

  try {
    let result = await getNamePost({a: 10, b: 20})
    console.log(1, result)
  } catch (error) {
    console.log(2, error)
  }
})()

可以在 API 中自定义配置

有一些特殊接口,当它出错时,我们不希望它和其他接口一样,弹出统一提示,而且希望偷摸去做一些其他事。

可以在写 api 时,写入一些参数:

api.js 文件中:

js 复制代码
// 引入上面那个服务
import service from './request'

export const getNameGet = (data, toBack = false) => (
  service({
    url: '/getNameGet',
    method: 'get',
    params: data,
    notTip: true, // 自定义参数
    toBack, // 调用接口时临时配置
  })
);

request.js 中:

以下能拿到数据,自然就能自己根据判断,去做一些其他处理。

js 复制代码
// 其他代码...

// 请求前的拦截
service.interceptors.request.use(config => {
  console.log(config.notTip) // 拿到数据
  console.log(config.toBack) // 拿到数据
  // 其他代码...
  return config;
}, Promise.reject)

// 响应后的拦截
service.interceptors.response.use(res => {
  console.log(res.config.notTip) // 拿到数据
  console.log(res.config.toBack) // 拿到数据
  // 其他代码...
  return Promise.reject(res)
}, err => {
  console.log(err.config.notTip) // 拿到数据
  console.log(err.config.toBack) // 拿到数据
  // 其他代码...
  return Promise.reject(err)
})
// 其他代码...

Axios停止接口调用

无论是Vue还是React,使用了Axios后,在某些时候,需要停止已经发出、正在pedding等待响应的接口,比如在跳页时。

下面就是Vue在跳页时紧急停止axios接口调用的方法,React同理。

但虽说前端停止了接口调用,但如果后台已经处理完了这个接口的数据和操作,那还是无法撤回的。

js 复制代码
// store.js 存放某处的全局变量
export const store = {
  source: {
    token: null,
    cancel: null
  }
}

// axios 配置中
import store from '@/store.js'
http.interceptors.request.use(config => {
  config.cancelToken = store.source.token
  return config
}, err => {
  return Promise.reject(err)
})

// router的配置文件中
import Axios from 'axios';
import store from '@/store.js'
router.beforeEach((to, from, next) => {
  const CancelToken = Axios.CancelToken
  store.source.cancel && store.source.cancel()
  store.source = CancelToken.source()
  next()
})