zoukankan      html  css  js  c++  java
  • vue:axios拦截器

    拦截器分两类:请求拦截器和响应拦截器

    一、请求拦截器

    在请求发出之前设置一些信息。比如说设置请求头,

    use方法参数即第一个函数的形参中通过config来做信息的配置,配置完之后,必须把config返回,这样才能完成拦截器的功能。第二个函数用于处理错误的信息。

    用拦截器的方式配置请求头会更加灵活。拦截器中可以通过config获取更多的信息,比如url地址,这样就可以根据url作出判断,某些url可以添加一个请求头,而别的url不添加请求头。

     

    通过拦截器,我们可以控制所有的请求。

    下面来分析项目中的请求拦截器

    // request拦截器
    service.interceptors.request.use(
    
      config => {
        if (store.getters.token) {
          config.headers['Authorization'] = getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
        }
    
        // params参数编码
        let url = config.url;
        console.log(url)
    
        if (config.params) {
          console.log(config.params)
          url += '?';
          let keys = Object.keys(config.params);
          console.log(keys)
          for (let key of keys) {
            if (config.params[key] !== null && config.params[key] !== "") {
              url += `${key}=${encodeURIComponent(config.params[key])}&`;
            }
          }
          console.log(url)
          url = url.substring(0, url.length - 1);
          console.log(url)
          config.params = {};
        }
        config.url = url;
        return config;
      },
      error => {
        console.log(error) // for debug
        Promise.reject(error)
      }
    )

    下面来分析代码:

    
    
    medMaterial.getAuditorsByPrepareId({
    prepareId: edit.id,
    productId: edit.productId,
    enterpriseId: edit.enterpriseId1
    }).then(res => {
    if (res.success) {
    .........

    js中的代码为:

    getAuditorsByPrepareId(query){
        return request({
          url: '/medMaterial/getAuditorsByPrepareId',
          method: 'post',
          params: query
        })
      },

    后台:

    @PostMapping("/getAuditorsByPrepareId")
        public Result getAuditorsByPrepareId(@RequestParam(required = false) String productId,@RequestParam String enterpriseId,@RequestParam String prepareId) {
        。。。。。
        }

    当我们发出上面的一个请求,请求拦截器就会将参数的值进行编码,然后将参数通过?号拼接到url中。拼接后的url如下所示:

    http://localhost:8008/api/medMaterial/getAuditorsByPrepareId?prepareId=444&productId=20663&enterpriseId=81

    1、先判断token是否存在,如果存在,让每个url都添加请求头Authorization

    2、通过config获取url:/medMaterial/getAuditorsByPrepareId

    3、通过url获取params:{prepareId: 444, productId: 20663, enterpriseId: 81}

    4、先给url拼接一个?号:/medMaterial/getAuditorsByPrepareId?

    5、获取config中参数params中所有的key即keys:["prepareId", "productId", "enterpriseId"]

    6、遍历keys,如果params中key的值不为null或不为空字符串,先对key的值进行encodeURIComponent编码,编码后的结果放入${}中,key不编码。由于该方法encodeURIComponent不会对 ASCII 字母和数字进行编码,故对value进行编码后的结果url仍然为:/medMaterial/getAuditorsByPrepareId?prepareId=444&productId=20663&enterpriseId=81&

    7、去掉最后的&得到的url:/medMaterial/getAuditorsByPrepareId?prepareId=444&productId=20663&enterpriseId=81

    再比如备货详情中,点击查看按钮,发送的请求如下:

    const query = { type: row.type,materialName: row.materialName,materialCode: row.materialCode,batch: row.originalBase }
            console.log(query)
            prepare.queryFullChain(query).then(response => {
              console.log(response)
              if(response.success){

    js中的代码如下:

    queryFullChain(query) {
        return request({
          url: '/prepare/queryFullChain',
          method: 'post',
          params: query
        });
      },

    1、先判断token是否存在,如果存在,让每个url都添加请求头Authorization

    2、通过config获取url:/prepare/queryFullChain

    3、通过url获取params:{type: "0", materialName: "天麻", materialCode: "TM001", batch: "11"}

    4、先给url拼接一个?号:/prepare/queryFullChain?

    5、获取config中参数params中所有的key即keys:["type", "materialName", "materialCode", "batch"]

    6、遍历keys,如果params中key的值不为null或不为空字符串,先对key的值进行encodeURIComponent编码,编码后的结果放入${}中,key不编码。由于该方法encodeURIComponent不会对 ASCII 字母和数字进行编码,故对value进行编码后的结果url为:/prepare/queryFullChain?type=0&materialName=%E5%A4%A9%E9%BA%BB&materialCode=TM001&batch=11&,注意对中文进行了编码。

    7、去掉最后的&得到的url:/prepare/queryFullChain?type=0&materialName=%E5%A4%A9%E9%BA%BB&materialCode=TM001&batch=11

    以上分析可知:当在向后台发起请求时,config中存在params时,参数会被拼接到url中,后台只能用@RequestParam来接收参数。

    当向后台发起请求时,没有params,而是data,如下所示:

    saveList(query) {
        return request({
          url: '/intelDrugStore/saveList',
          method: 'post',
          data: query
        })
      },

    请求参数在JSON中,故后台只能用@RequestBody接收参数。

    后台

    @PostMapping("/saveList")
        @ResponseBody
        public Result saveList(@RequestBody List<IntelDrugStore> listData) {
        。。。。
        }

    二、响应拦截器

    浏览器在获取响应数据之前对数据做一些加工处理。

    Use方法的第一个参数即第一个函数的形参res表示后台返回的具体数据信息。res并不是实际的数据,而是axios包装的对象,通过对象中的data才能拿到数据。这和之前获取后台数据的时候是一样的。

    如果在调用接口的时候,只关心实际的数据而不需要包装对象,故可以集中的在响应拦截器做一些加工。如下所示

    这样再次发请求调接口的时候,最终拿到的就是实际的数据了。

    这样的话,以后我们调用任何接口,所有then当中得到的数据,都是我们实际需要的后台返回来的数据。不需要再通过点data的方式获取数据了

     项目中的响应拦截器如下:

    // response 拦截器
    service.interceptors.response.use(
      response => {
        /**
         * code为非20000是抛错 可结合自己业务进行修改
         */
        const headers = response.headers
        // 此类为下载流文件,不拦截
        if (headers['content-type'] === 'application/octet-stream;charset=utf-8') {
          return response
        }
        if (headers['content-type'] === 'arrayBuffer;charset=UTF-8') {
          return response
        }
        const res = response.data
        if (res.code !== 1) {
          if(res.code === 301){
            Message({
              message: res.msg,
              type: 'error',
              duration: 5 * 1000
            })
            location.reload()
          }
    
          // 50008:非法的token; 50012:其他客户端登录了;  50014:Token 过期了;
          if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
            MessageBox.confirm(
              '你已被登出,可以取消继续留在该页面,或者重新登录',
              '确定登出',
              {
                confirmButtonText: '重新登录',
                cancelButtonText: '取消',
                type: 'warning'
              }
            ).then(() => {
              store.dispatch('FedLogOut').then(() => {
                location.reload() // 为了重新实例化vue-router对象 避免bug
              })
            })
          }
          return Promise.reject('请重新登录')
        } else {
          return response.data
        }
      },
      error => {
        console.log('err' + error) // for debug
        Message({
          message: '请求超时,请联系管理员!',
          type: 'error',
          duration: 5 * 1000
        })
        return Promise.reject(error)
      }
    )

     响应拦截器代码分析:

    1、由于后台EasyPOIUtils在下载Excel时,设置了响应头,如下:

    response.setHeader("Content-type", "text/html;charset=UTF-8");
                response.setCharacterEncoding("utf-8");//设置编码集,文件名不会发生中文乱码
    
                response.setContentType("application/force-download");//
                response.setHeader("content-type", "application/octet-stream");
                response.addHeader("Content-Disposition", "attachment;fileName=" + new String(fileName.getBytes(), "utf-8"));// 设置文件名
                response.addHeader("Content-Length", "" + file.length());
                response.setHeader("Access-Control-Allow-Origin", "*");

     故响应拦截器不拦截。

    2、由于在下载图片时,PtsFileController中设置了响应头,

    @RequestMapping(value = "/picDownload")
        public void picDownload(  String filePath,HttpServletResponse response)throws Exception{
            File file = new File(filePath);
            ServletOutputStream  out = null ;
            BufferedInputStream buf  = null ;
            response.setHeader("Content-Type","arrayBuffer");
            try{
                buf = new BufferedInputStream(new FileInputStream(file));
                out = response.getOutputStream();
                byte[] buffer =  new byte[1024];
                while(buf.read(buffer) != -1){
                    out.write(buffer);
    
                }
                out.flush();
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                if(buf!= null){
                    buf.close();
                }
                if(out != null){
                    out.close();
                }
            }
        }

     故响应拦截器不拦截。

    3、response并不是实际的数据,而是axios包装的对象,通过对象中的data才能拿到数据.

    4、由于后台ResultCode类中设置请求成功的code为1,

    /**
         * 请求成功Result
         */
        public static final int SUCCESS=1;

    当我们要返回数据给前端时,会设置code为success

    result.setSuccess(false);
                    result.setMsg("请修改密码后再登录!");
                    result.setCode(ResultCode.SUCCESS);
                    return result;

    此时res.code等于1,返回response.data给前端。

    如果res.code不等于1,由于后台设置了用户登录超时的code为301

    /**
         * 用户未登录或登录超时
         */
        public static final int NO_LOGIN = 301;

    当我们要返回数据给前端时,会设置ResultCode为NO_LOGIN

    User u = (User) redisService.get(pre + "TPS-TOKEN" + token);
                if (null == u) {
                    rs.setSuccess(false);
                    rs.setCode(ResultCode.NO_LOGIN);
                    rs.setMsg("非法登录或者token已失效!");
                    return rs;
                }

    此时res.code为301,则进行错误消息的提示,type为error,message为后台返回的消息,duration为持续的时长,这里为5s.

    location.reload()方法用于刷新当前文档。reload() 方法类似于你浏览器上的刷新页面按钮。

  • 相关阅读:
    计算表达式的值并输出,表达式由若干个数字和运算符(只包含加号和减号)构成(C版和python版)
    联机分析场景的关键特征(OLAP)
    C语言字符串去掉指定字符
    主机或者路由器是怎样知道应当在MAC帧的首部填入什么样的硬件地址?(ARP)
    linux虚拟机重置root密码
    python获取时间串的方法
    centos离线安装docker
    前端JavaScript规范
    JQuery中的Deferred-详解和使用
    垂直居中实用三种方式
  • 原文地址:https://www.cnblogs.com/zwh0910/p/14917981.html
Copyright © 2011-2022 走看看