RuoYi-vue框架之拦截器

本文详细介绍了RuoYi-vue框架中的前端拦截器,包括添加请求头的token、处理get请求参数、防止重复请求提交以及响应拦截器的逻辑。着重展示了如何在请求头中添加token,处理对象参数并检测重复提交。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

RuoYi-vue框架之前端拦截器

前端拦截器

前置拦截器

文件位置:/src/utils/request.js

// request拦截器
service.interceptors.request.use(config => {
  // 是否需要设置 token
  const isToken = (config.headers || {}).isToken === false
  // 是否需要防止数据重复提交
  const isRepeatSubmit = (config.headers || {}).repeatSubmit === false
  if (getToken() && !isToken) {
    config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
  }
  // get请求映射params参数
  if (config.method === 'get' && config.params) {
    let url = config.url + '?' + tansParams(config.params);
    url = url.slice(0, -1);
    config.params = {};
    config.url = url;
  }
  if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
    const requestObj = {
      url: config.url,
      data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
      time: new Date().getTime()
    }
    const sessionObj = cache.session.getJSON('sessionObj')
    if (sessionObj === undefined || sessionObj === null || sessionObj === '') {
      cache.session.setJSON('sessionObj', requestObj)
    } else {
      const s_url = sessionObj.url;                  // 请求地址
      const s_data = sessionObj.data;                // 请求数据
      const s_time = sessionObj.time;                // 请求时间
      const interval = 1000;                         // 间隔时间(ms),小于此时间视为重复提交
      if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) {
        const message = '数据正在处理,请勿重复提交';
        console.warn(`[${s_url}]: ` + message)
        return Promise.reject(new Error(message))
      } else {
        cache.session.setJSON('sessionObj', requestObj)
      }
    }
  }
  return config
}, error => {
    console.log(error)
    Promise.reject(error)
})
做了一下三件事情

1.在请求头中添加token
2.请求映射params参数
3.阻止重复请求提交

第一步见名知意

第二步,请求映射params参数

其实就是解析对象当get请求中带的参数中有对象或者数据中非默认值的时候,解析然后拼接成字符串
一下就是解析的代码

  // get请求映射params参数
  if (config.method === 'get' && config.params) {
  //请求为get且带有参数
    let url = config.url + '?' + tansParams(config.params);  //请求 + 参数处理(就是转换成字符串)
    url = url.slice(0, -1);   //去掉最后一位(应该是拼接的时候残留的拼接符号,所以要去掉) 
    config.params = {};   //复位 
    config.url = url;   //返回拼接处理完参数后的url
  }

其中tansParams可以看出来确实是转成字符串类型用&拼接,最后一位&要去掉

/**
* 参数处理
* @param {*} params  参数
*/
export function tansParams(params) {
  let result = ''
  for (const propName of Object.keys(params)) {
    const value = params[propName];
    var part = encodeURIComponent(propName) + "=";
    if (value !== null && value !== "" && typeof (value) !== "undefined") {
      if (typeof value === 'object') {
        for (const key of Object.keys(value)) {
          if (value[key] !== null && value[key] !== "" && typeof (value[key]) !== 'undefined') {
            let params = propName + '[' + key + ']';
            var subPart = encodeURIComponent(params) + "=";
            result += subPart + encodeURIComponent(value[key]) + "&";
          }
        }
      } else {
        result += part + encodeURIComponent(value) + "&";
      }
    }
  }
  return result
}

可以看出前端get请求参数做了统一的处理后面,有利于前后端传参的统一管理。
补充,对其他请求的参数处理

  if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
    const requestObj = {
      url: config.url,
      data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
      //JOSN对象转换成JSON字符串
      time: new Date().getTime()
    }

Json对象与Json字符串的相互转化

var  str = '{"name":"lin","sex":"男","age":"23"}';
var  strToObj = JSON.parse(str);   //转换成对象
console.log(strToObj);//{name:"lin",sex:"男",age:"23"}

var  str= JSON.stringify(strToObj); //转换成字符串
console.log(str);//{"name":"lin","sex":"男","age":"23"}  

第三步.阻止重复请求提交
const sessionObj = cache.session.getJSON('sessionObj')
    if (sessionObj === undefined || sessionObj === null || sessionObj === '') {
      cache.session.setJSON('sessionObj', requestObj)
    } else {
      const s_url = sessionObj.url;                  // 请求地址
      const s_data = sessionObj.data;                // 请求数据
      const s_time = sessionObj.time;                // 请求时间
      const interval = 1000;                         // 间隔时间(ms),小于此时间视为重复提交
      if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) {
        const message = '数据正在处理,请勿重复提交';
        console.warn(`[${s_url}]: ` + message)
        return Promise.reject(new Error(message))
      } else {
        cache.session.setJSON('sessionObj', requestObj)
      }
    }

响应拦截器

// 响应拦截器

service.interceptors.response.use(res => {
    // 未设置状态码则默认成功状态
    const code = res.data.code || 200;
    // 获取错误信息
    const msg = errorCode[code] || res.data.msg || errorCode['default']
    // 二进制数据则直接返回
    if (res.request.responseType ===  'blob' || res.request.responseType ===  'arraybuffer') {
      return res.data
    }
    if (code === 401) {
      if (!isRelogin.show) {
        isRelogin.show = true;
        MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => {
          isRelogin.show = false;
          store.dispatch('LogOut').then(() => {
            location.href = '/index';
          })
      }).catch(() => {
        isRelogin.show = false;
      });
    }
      return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
    } else if (code === 500) {
      Message({ message: msg, type: 'error' })
      return Promise.reject(new Error(msg))
    } else if (code === 601) {
      Message({ message: msg, type: 'warning' })
      return Promise.reject('error')
    } else if (code !== 200) {
      Notification.error({ title: msg })
      return Promise.reject('error')
    } else {
      return res.data
    }
  },
  error => {
    console.log('err' + error)
    let { message } = error;
    if (message == "Network Error") {
      message = "后端接口连接异常";
    } else if (message.includes("timeout")) {
      message = "系统接口请求超时";
    } else if (message.includes("Request failed with status code")) {
      message = "系统接口" + message.substr(message.length - 3) + "异常";
    }
    Message({ message: message, type: 'error', duration: 5 * 1000 })
    return Promise.reject(error)
  }
)

常规

后端拦截器

地址 framewor/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
    {
        if (handler instanceof HandlerMethod)  //判断请求类型,不是方法就放行
        {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class);   //获取请求controller方法上的注解
            if (annotation != null)                        //有没有防重提交
            {
                if (this.isRepeatSubmit(request, annotation))    
                {
                    AjaxResult ajaxResult = AjaxResult.error(annotation.message());
                    ServletUtils.renderString(response, JSON.toJSONString(ajaxResult));
                    return false;
                }
            }
            return true;
        }
        else
        {
            return true;
        }
    }
### Ruoyi-Vue-Plus 前端拦截器实现与配置 #### 1. 拦截器的作用 前端拦截器主要用于处理请求和响应的数据预处理工作,比如在发送请求前添加认证令牌、修改请求头,在接收响应后统一处理错误状态码等。对于 `Ruoyi-Vue-Plus` 来说,其基于 Vue 和 Axios 构建,因此可以利用 Axios 提供的拦截器机制来完成这些需求。 #### 2. Axios 请求与响应拦截器基础 Axios 是一个流行的 HTTP 客户端库,支持浏览器环境以及 Node.js 环境下的异步数据交互操作。通过 Axios 的拦截器功能,可以在请求发出之前或者接收到服务器返回的结果之后执行特定逻辑[^3]。 以下是 Axios 中设置全局拦截器的方式: ```javascript import axios from 'axios'; // 创建实例 const instance = axios.create({ baseURL: process.env.VUE_APP_BASE_API, // 设置默认的基础路径 timeout: 5000 // 超时时间设为5秒 }); // 添加请求拦截器 instance.interceptors.request.use( (config) => { const token = localStorage.getItem('token'); // 获取存储中的Token if (token) { config.headers['Authorization'] = `Bearer ${token}`; // 将 Token 添加到请求头部 } return config; }, (error) => Promise.reject(error) ); // 添加响应拦截器 instance.interceptors.response.use( (response) => response.data, (error) => { if (error.response && error.response.status === 401) { // 处理未授权情况 console.error('Unauthorized access'); window.location.href = '/login'; // 如果是权限不足跳转至登录页 } return Promise.reject(error); } ); export default instance; ``` 上述代码展示了如何创建一个 Axios 实例并为其绑定请求和响应拦截器。其中,请求拦截器负责向每次请求附加必要的身份验证信息;而响应拦截器则用于捕获异常状况(如无权访问资源),从而采取适当措施。 #### 3. 结合 RuoYi-Vue-Plus 使用场景分析 根据引用描述可知,`RuoYi-Vue-Plus` 已经集成了多种第三方工具和技术栈,其中包括 Sa-Token 认证框架[^2]。这意味着该系统的前后端分离架构下可能已经内置了一套完整的用户会话管理方案。开发者只需按照既定模式扩展即可满足实际业务需求。 具体来说,在项目初始化阶段通常会在 src/main.js 或者专门的服务文件夹 service/api 下定义好公共 API 方法及其对应的拦截策略。例如下面这段伪代码片段演示了如何加载初始配置并将它们应用在整个应用程序生命周期当中: ```javascript import Vue from 'vue'; import App from './App.vue'; import router from './router'; import store from './store'; import apiClient from '@/utils/request'; // 自定义封装好的API调用模块 Vue.config.productionTip = false; new Vue({ router, store, render: h => h(App), }).$mount('#app'); function setupInterceptors() { apiClient.interceptors.request.use(config => { let userStoreData = JSON.parse(localStorage.getItem("userInfo")); if(userStoreData){ config.headers["X-AUTH-TOKEN"] = `${userStoreData.token}`; }else{ delete config.headers["X-AUTH-TOKEN"]; } return config; }, err=>Promise.reject(err)); apiClient.interceptors.response.use(res=>{ switch(res.code){ case 200: break; case 401: alert("Session expired! Please log in again."); location.reload(); break; default : throw new Error(`Error Code:${res.code}`); } return res; },err=>{throw err}); } setupInterceptors(); // 初始化拦截器 ``` 此段脚本说明了当启动 Vue 应用程序时即刻激活自定义化的网络通信层设定——不仅限于简单的 GET/POST 动作本身,还包括围绕安全性和用户体验优化方面的考量。 #### 4. 总结 综上所述,`Ruoyi-Vue-Plus` 的前端拦截器可以通过 Axios 插件轻松实现复杂的功能定制化开发过程。无论是为了增强安全性还是简化跨域资源共享等问题解决起来都变得简单高效许多。同时得益于现代 JavaScript 生态圈丰富的插件体系支撑使得整个工程更加灵活可控。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值