首页 > 技术文章 > vue中axios使用

shine-lovely 2020-04-26 16:53 原文

axios的特点
1. 支持浏览器和node.js
2. 支持promise
3. 能拦截请求和响应
4. 能转换请求和响应数据
5. 能取消请求
6. 自动转换JSON数据
7. 浏览器端支持防止CSRF(跨站请求伪造)

PS:防止CSRF:
让每个请求都带一个从cookie中拿到的key, 根据浏览器同源策略,假冒的网站是拿不到cookie中得key的,这样,后台就可以轻松辨别出这个请求是否是用户在假冒网站上的误导输入,从而采取正确的策略。

 

axios提问

  1. 如何将axios异步请求同步化处理?
//使用 asyns/await 
async getHistoryData (data) {
 try {
   let res = await axios.get('/api/survey/list/', {
     params: data
   })
   this.tableData = res.data.result
   this.totalData = res.data.count
 } catch (err) {
   console.log(err)
   alert('请求出错!')
 }
}
  1. 为何官方推荐使用axios而不用vue-resource

    Vue1.x中,官方推荐使用的ajax库是vue-resource。到了Vue2.x,官方(尤大)推荐的ajax库改为了Axios,按照说法是因为已有一个更完备的轮子,就不需要造一个新的。

  2. 你了解axios的原理吗?有看过它的源码吗?     

  3. 你有封装过axios吗?主要是封装哪方面的?
  • 封装request.js, 导出service。

    import axios from 'axios'
    import {getToken} from '@/utils/auth'

    
    

    const service = axios.create({
    baseURL: process.env.BASE_API, // url = base url + request url
    timeout: 5000
    })

    
    


    // request拦截器
    service.interceptors.request.use(
    config => {
    // do something before request is sent
    config.headers['x-access-token'] = getToken()
    return config
    },
    error => {
    // do something with request error
    console.log(error) // for debug
    return Promise.reject(error)
    }
    )

    
    


    // response拦截器
    service.interceptors.response.use(
    response => {
    /**
    * 1.code为非000000是抛错 可结合自己业务进行修改
    * 2.response 是 http 封装的实体类,response.data 使我们真正的响应数据
    */
    // const res = response.data
    // console.log("拦截前的response:",response);
    return response.data
    },
    error => {
    console.log('err' + error) // for debug
    return Promise.reject(error)
    }
    )
    export default service

     

     

  •  创建http.js。 导出http

    /**
    * Created by czx on 2020/1/17.
    */
    import request from './request'
    import qs from 'qs'
    // qs.parse()将URL解析成对象的形式;qs.stringify()将对象 序列化成URL的形式,以&进行拼接

    
    

    const Http = {}
    Http.request = request

    
    

    /**
    * @desc Http表单方式提交
    * @param url 请求的url 参数 /loginAsync.do
    * @parrm method 请求方法类型 类似post get
    * @param params {} 参数对象 data: {username: username, password: password},
    */
    Http.commitForm = function (url, params, config = {}) {
    const _config = {
    url,
    method: 'post',
    data:params,
    headers: {
    'Content-Type': 'application/x-www-form-urlencoded'
    },
    transformRequest: params => qs.stringify(params, { arrayFormat: 'repeat' })// 修改数组传递格式
    }
    config = Object.assign(config, _config)
    return request(config)
    }

    
    

    /*
    * JSON格式的接口,默认为post请求
    * 传入 config={method: 'get'}则变为get请求
    */
    Http.Json = function(url, params, config = {}) {
    const _config = {
    method: 'post',
    url: url,
    data: params,
    headers: {
    'Content-Type': 'application/json'
    }
    }
    config = Object.assign(_config, config)
    return request(config)
    }

    
    

    Http.JsonPic = function(url, params) {
    return request.post(url, params)
    }

    
    

    Http.post = function(url, params, config = {}) {
    const _config = {
    method: 'post',
    url: url,
    data: params,
    headers: {
    'Content-Type': 'application/x-www-form-urlencoded'
    },
    transformRequest: function(params) {
    return qs.stringify(params, { arrayFormat: 'repeat' }) // 修改数组传递格式
    }
    }
    config = Object.assign(_config, config)
    return request(config)
    }

    
    

    Http.get = function(url, params, config = {}) {
    const _config = {
    method: 'get',
    url: url,
    params: params,
    headers: {
    'Content-Type': 'application/x-www-form-urlencoded'
    },
    paramsSerializer: function(params) {
    return qs.stringify(params, { arrayFormat: 'repeat' }) // 修改数组传递格式
    }
    }
    config = Object.assign(_config, config)
    return request(config)
    }

    
    

    /*Http.mapUrl = function(url, params) {
    if (typeof params === 'object') {
    params = encodeUrlParam(params)
    } else {
    params = null
    }
    if (params) {
    if (url.indexOf('?') >= 0) {
    url += '&' + params
    } else {
    url += '?' + params
    }
    }
    if (!process.env.BASE_API || process.env.BASE_API === '/' || process.env.BASE_API === '') {
    return window.location.origin + url
    }
    return process.env.BASE_API + url
    }*/

    
    

    export default Http

     

  • 其中request.js中用到的 {getToken} 方法,在auth.js 中封装

    import Cookies from 'js-cookie'
    const TokenKey = 'x-access-token'

    
    

    export function getToken() {
    return Cookies.get(TokenKey)
    }

    
    

    export function setToken(token) {
    return Cookies.set(TokenKey,token)
    }

    
    

    export function removeToken() {
    return Cookies.remove(TokenKey)
    }

     

  • 在业务api使用
    import Http from '@/utils/http.js'
    
    export function login(param) {
      return Http.post('/login',param)
    }
    
    export function register(param) {
      return Http.post('/register',param)
    }

     

  • 在vue页面中引用
     import {login, register} from '@/api/login'

     

       5.  如何中断(取消)axios的请求?

  • 在项目中碰到了短时间多次请求的情况,为了更好地用户体验和性能,需要取消上次axios请求  
  • 前端发起请求后,后端还未全部返回请求数据时,终止请求,不再返回数据。
    • 栗子: 【附件上传】:前端上传一个1G大小的压缩文件时,耗时大概一两分钟,在上传到50%的时候,用户要取消上传,此时,就要执行如题所探讨的终止请求动作。
    • 解决方案:利用axios请求的config参数,向axios添加一个包含cancelToken的config配置对象。
      export default {
          data() {
            return {cancelTokenFn: null}
          },
          methods: {
            cancelTokenDone() {
              const _this = this
              const CancelToken = axios.CancelToken;
              this.cancelTokenFn && this.cancelTokenFn();
              this.cancelTokenFn = null;
              axios.get('url', {
                cancelToken: new CancelToken(function executor(c) {
                  _this.cancelTokenFn = c
                })
              })
                .then(function (response) {
                  const data = response.data;
                  console.log(data);
                })
                .catch(function (error) {
                  console.log(error)
                })
            }
          }
        }
      this.cancelTokenFn()

       

  1. axios怎么解决跨域的问题?

    • 如果 server 端是自己开发的,那么修改相关代码支持跨域即可。如果不是自己开发的,那么可以自己写个后端转发该请求,用代理的方式实现。

    • 跨域这个行为是浏览器禁止(浏览器不允许当前页面的所在的源去请求另一个源的数据)的,但是服务端并不禁止

    • 源指协议、端口、域名。只要这个3个中有一个不同就是跨域。 这里列举一个经典的列子:

      协议跨域:
      http://a.baidu.com访问https://a.baidu.com;
      端口跨域:
      http://a.baidu.com:8080访问http://a.baidu.com:80;
      域名跨域:
      http://a.baidu.com访问http://b.baidu.com;
      
    • 关键字:Access-control-Allow-origin、跨域

    • 几种解决跨域的方法

    • A) 开发模式下,可以在config中配置proxyTable即可

       
    • B) 服务端基于spring实现


       
    • C) CORS:①即跨源资源共享,它定义了一种浏览器和服务器交互的方式来确定是否允许跨域请求。 ②它是一个妥协,有更大的灵活性,但比起简单地允许所有这些的要求来说更加安全。③但是CORS也具有一定的风险性,比如请求中只能说明来自于一个特定的域但不能验证是否可信,而且也容易被第三方入侵。 ④这里一般需要后端配合,开启cors。一般各种语言都有类似的包。比如NodeJS的koa2-cors

       
    • D) Nginx代理proxy

    • E) express代理

      image
    • 请求头自动携带cookie时:config中配置withCredentials:true,否则为false【看到有人说:withCredentialstrue的情况下,后端要设置Access-Control-Allow-Origin为你的源地址,例如http://localhost:8080,不能是*,而且还要设置header('Access-Control-Allow-Credentials: true')

    • config中配置qs:避开ajax信使请求,并兼容Android

    import Qs from 'qs'
    
 
 

推荐阅读