解决vue中axios使用AbortController取消的请求无法重新启动

文章讲述了在Vue项目中遇到的问题,即前端使用长连接时,在切换页面后无法正确关闭和重新发起请求。通过引入AbortController解决长连接的取消和重新发起问题,将控制器实例化放在data里,以便在页面生命周期中正确管理请求。

需求:有个项目需要使用长连接。前端发起请求,如果有新消息就马上返回数据,如果没有就等待(20s)至超时,重新发起请求。

问题:当前页面发起的请求,因为是长连接,当切换到其他页面该连接还在等待,然后继续请求,无法马上结束。

解决:这个时候就需要用到axios的AbortController来结束请求。

官方示例:

const controller = new AbortController();

axios.get('/foo/bar', {
   signal: controller.signal
}).then(function(response) {
   //...
});
// cancel the request
controller.abort()

新问题:直接用官方的这个方法,在切换页面的时候在当前页面周期beforeDestroy里面执行一下关闭,就关闭了这个长连接。但是出现了新的问题,就是重新进入这个页面的时候无法重新发起请求了。

解决:后来略作小改动,解决了:

data() {
    return {
      ...
      controller: new AbortController(),
    };
  },

将controller放到data里面去定义,下面使用的时候用this.controller。这样写之后就可以重新发起请求了。

 

我最开始写的时候是放到data外面了,直接const controller = new AbortController(); 就出现问题了。看来在vue中要稍微改变一下。

/** * OpenWebUI 专用 axios 配置 * 用于处理与 OpenWebUI 相关的聊天、模型、会话等接口请求 */ import axios from 'axios'; import { ElMessage, ElNotification } from 'element-plus'; import NProgress from 'nprogress'; import 'nprogress/nprogress.css'; import { getOpenWebUIToken, setOpenWebUIToken, removeOpenWebUIToken } from '@/utils/auth-openwebui'; // 独立存储OpenWebUI的token import { getToken } from '@/utils/auth'; import store from '@/store/'; // 配置 NProgress 进度条(可根据需求调整) NProgress.configure({ showSpinner: false, trickleSpeed: 100 }); // 1. 创建 OpenWebUI 专用 axios 实例 const axiosChats = axios.create({ // OpenWebUI 基础地址(建议从环境变量或配置文件读取) baseURL: import.meta.env.VITE_OPENWEBUI_BASE_URL, timeout: 60000, // 聊天接口超时时间设长一些(60秒) headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, withCredentials: true // 根据 OpenWebUI 部署配置决定是否开启跨域凭证 }); // 2. 请求拦截器:处理 token、请求头等 axiosChats.interceptors.request.use( (config) => { // 启动进度条 NProgress.start(); // 添加 OpenWebUI 认证 token(如果存在) const token = getOpenWebUIToken(); if (token) { config.headers['Authorization'] = `Bearer ${token}`; } const tokenPC = getToken(); if (tokenPC) { config.headers['x-saber-token'] = `Bearer ${tokenPC}`; } // 针对 OpenWebUI 特定接口的特殊处理 const specialEndpoints = { '/v1/chat/completions': { // 流式聊天接口 responseType: 'stream', // 必须设置为 stream 以接收流式响应 headers: { 'Accept': 'text/event-stream' } } }; // 应用特殊接口配置 const endpoint = Object.keys(specialEndpoints).find(key => config.url?.includes(key)); if (endpoint) { Object.assign(config, specialEndpoints[endpoint]); } return config; }, (error) => { NProgress.done(); ElMessage.error('请求准备失败:' + error.message); return Promise.reject(error); } ); // 3. 响应拦截器:处理响应、错误、token刷新等 axiosChats.interceptors.response.use( (response) => { NProgress.done(); const config = response.config; // 1. 处理流式响应(聊天接口专用) if (config.responseType === 'stream') { // 直接返回流对象,由调用方处理流式数据 return response; } // 2. 处理普通 JSON 响应(如模型列表、会话管理等) const { code, message, data } = response.data; // OpenWebUI 成功码通常为 200 或 0(根据实际接口调整) if (code === 200 || code === 0 || !code) { // 自动保存 token(如果接口返回新 token,如登录后) if (data?.access_token) { setOpenWebUIToken(data.access_token); } return response.data; // 直接返回核心数据,简化调用 data || } // 3. 业务错误处理 ElMessage.error(message || '操作失败,请稍后重试'); return Promise.reject(new Error(message || 'OpenWebUI 接口返回错误')); }, (error) => { NProgress.done(); const response = error.response; // 分类处理错误 if (!response) { ElMessage.error('网络错误:无法连接到 OpenWebUI 服务'); return Promise.reject(error); } const status = response.status; const errorMsg = response.data?.message || '请求失败'; switch (status) { // 401:token 过期或未授权 case 401: removeOpenWebUIToken(); // 清除失效 token ElNotification({ title: '认证失效', message: '请重新登录 OpenWebUI', type: 'error', duration: 5000 }); store.dispatch('chat/AutoLoginAi'); // 401登录后刷新本页 setTimeout(() => { window.location.reload(); }, 1500); break; // 403:权限不足 case 403: ElMessage.error('权限不足:' + errorMsg); break; // 404:接口不存在 case 404: ElMessage.error('接口不存在:' + error.config.url); break; // 500+:服务端错误 case 500: case 502: case 503: ElMessage.error('OpenWebUI 服务异常:' + errorMsg); break; // 其他错误 default: ElMessage.error(`请求错误 [${status}]:${errorMsg}`); } return Promise.reject(error); } ); // 4. 导出常用的 OpenWebUI 接口封装(可选,简化页面调用) export const openWebUIApi = { /** * 流式聊天接口(核心) * @param {Object} params - 聊天参数 * @param {string} params.model - 模型名称(如 gpt-3.5-turbo) * @param {Array} params.messages - 消息列表([{role: 'user', content: '你好'}]) * @param {boolean} params.stream - 是否流式返回(默认 true) * @returns {Promise} - 流式响应对象 */ chatCompletions: (params) => { return axiosChats.post('/v1/chat/completions', { stream: true, // 强制流式返回(OpenWebUI 聊天默认支持) ...params }); }, /** * 获取模型列表 * @returns {Promise} - 模型列表数据 */ getModels: () => { return axiosChats.get('/v1/models'); }, /** * 保存会话 * @param {Object} session - 会话数据(包含消息、标题等) * @returns {Promise} - 保存结果 */ saveSession: (session) => { return axiosChats.post('/v1/sessions', session); }, /** * 登录 OpenWebUI(如果需要认证) * @param {Object} credentials - 登录凭证(用户名、密码) * @returns {Promise} - 登录结果 */ login: (credentials) => { return axiosChats.post('/v1/auth/login', credentials); } }; // 导出 axios 实例和接口封装(按需使用) export default axiosChats; 为何流对象是流完成后才触发
最新发布
11-13
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值