前端 Axios 深度封装实战:拦截器 + 文件处理 + 业务接口统一管理

前端 Axios 深度封装实战:拦截器 + 文件处理 + 业务接口统一管理

嘿,开发的小伙伴们!今天咱来好好唠唠Axios,这可是在前端数据请求领域相当火的一个工具库。我第一次用Axios的时候,就被它的简洁易用和强大功能给吸引住了,感觉像是找到了一个能帮我轻松搞定数据请求的得力助手。

注:章节 1-4 是通过 AI 生成的入门介绍,人工进行了审核和勘误,如已比较熟悉可跳过,章节 5 是纯人工创作,结合真实项目详细说明如何封装与使用。

一、Axios是什么

Axios本质上是一个基于Promise的HTTP客户端,主要用于浏览器和Node.js环境。它就像是一座桥梁,负责在前端应用和后端服务器之间传递数据。无论是向服务器发送GET、POST、PUT、DELETE等各种请求,还是处理服务器返回的响应,Axios都能轻松应对。

想象一下,你的前端应用就像一个热闹的集市,各种组件都需要从服务器获取数据来展示,比如商品信息、用户资料等等。Axios就是那个勤劳的“采购员”,它穿梭于集市(前端应用)和仓库(服务器)之间,按需获取数据,确保每个组件都能及时拿到所需信息。

二、Axios的特点

1. 简洁易用的API

Axios的API设计得非常简洁明了,上手特别容易。不管是简单的GET请求,还是复杂的POST请求带各种参数和头信息,都能通过几行代码搞定。例如,发送一个简单的GET请求获取数据:

axios.get('/api/data').then(response=>{ console.log(response.data);}).catch(error=>{ console.error('请求出错:', error);});

这里通过axios.get方法指定请求的URL,然后使用.then来处理成功的响应,.catch来捕获可能出现的错误。代码简洁易懂,即使是刚接触前端开发的新手也能很快掌握。

2. 支持Promise

Axios基于Promise实现,这使得异步操作变得更加优雅。Promise提供了一种链式调用的方式,可以方便地处理多个异步操作的先后顺序和依赖关系。比如,你可能需要先获取用户信息,然后根据用户信息再获取用户的订单列表,使用Axios和Promise可以这样写:

axios.get('/api/user').then(userResponse=>{const userId = userResponse.data.id;return axios.get(`/api/orders?userId=${userId}`);}).then(ordersResponse=>{ console.log('用户订单列表:', ordersResponse.data);}).catch(error=>{ console.error('请求出错:', error);});

通过链式调用.then,可以清晰地看到数据请求的流程,代码逻辑一目了然。

3. 拦截器功能

Axios的拦截器功能非常强大,它允许我们在请求发送前和响应接收后对数据进行统一处理。比如说,在每个请求发送前添加一些通用的头信息,或者在响应数据返回后进行统一的错误处理。

添加请求拦截器:

axios.interceptors.request.use(config=>{// 在发送请求前做些什么,比如添加token config.headers.Authorization =`Bearer ${localStorage.getItem('token')}`;return config;},error=>{// 对请求错误做些什么return Promise.reject(error);});

添加响应拦截器:

axios.interceptors.response.use(response=>{// 对响应数据做些什么,比如统一处理错误码if(response.data.errorCode){ console.error('服务器返回错误:', response.data.errorMessage);}return response;},error=>{// 对响应错误做些什么return Promise.reject(error);});

拦截器可以大大提高代码的复用性和可维护性,避免在每个请求和响应处理中重复编写相同的逻辑。

4. 支持浏览器和Node.js

Axios的适用范围很广,既能在浏览器环境中使用,也能在Node.js服务器端使用。这意味着无论是开发前端的Web应用,还是后端的Node.js服务,Axios都能派上用场。在前后端分离的项目中,Axios可以作为前后端数据交互的统一解决方案,方便又高效。

三、Axios的使用场景

1. 数据获取与展示

这是Axios最常见的使用场景。在前端页面中,经常需要从服务器获取数据并展示给用户,比如新闻列表、商品详情等。通过Axios发送GET请求获取数据,然后将数据渲染到页面上。

2. 表单提交

当用户在前端填写表单并提交时,通常需要将表单数据发送到服务器进行处理。Axios的POST请求可以轻松实现这一功能,将表单数据以合适的格式(如JSON或FormData)发送给服务器。

const formData =newFormData(); formData.append('username','JohnDoe'); formData.append('password','123456'); axios.post('/api/login', formData).then(response=>{ console.log('登录成功:', response.data);}).catch(error=>{ console.error('登录失败:', error);});

3. 文件上传

在一些应用中,需要实现文件上传功能。Axios同样可以胜任,通过POST请求将文件数据发送到服务器。

const fileInput = document.getElementById('fileInput');const file = fileInput.files[0];const formData =newFormData(); formData.append('file', file); axios.post('/api/upload', formData,{headers:{'Content - Type':'multipart/form-data'}}).then(response=>{ console.log('文件上传成功:', response.data);}).catch(error=>{ console.error('文件上传失败:', error);});

四、常见问题

1. CORS跨域问题

在前后端分离开发中,经常会遇到CORS(跨域资源共享)问题。当浏览器向不同源的服务器发送请求时,会受到同源策略的限制,导致请求失败。解决这个问题通常需要在后端服务器进行配置,允许前端应用的域名进行跨域访问。

以Node.js和Express为例,可以使用cors中间件来解决:

const express =require('express');const cors =require('cors');const app =express(); app.use(cors());// 其他路由和中间件配置 app.listen(3000,()=>{ console.log('服务器启动在端口3000');});

2. 错误处理

虽然Axios提供了.catch来捕获错误,但有时候错误信息可能不够详细,不利于调试。可以在响应拦截器中对错误进行更详细的处理,比如记录错误日志、弹出友好的错误提示给用户等。

axios.interceptors.response.use(null,error=>{ console.error('响应错误:', error.config);if(error.response){ console.error('状态码:', error.response.status); console.error('响应数据:', error.response.data);}elseif(error.request){ console.error('请求已发出,但没有收到响应:', error.request);}else{ console.error('请求出错:', error.message);}// 弹出友好的错误提示给用户alert('请求出错,请稍后重试');return Promise.reject(error);});

五、项目实战

以上为 AI 生成的入门介绍,人工进行了审核和修订。

在实际项目使用过程中,需要根据自己的需求,进行设计与二次封装,接下来重点说一说。

定义 service 类

首先,我们定义一个名为 service 的类,继承AxiosInstance,主要目的是进行全局性设置,如后端的 API 接口地址,请求超时时间等,类定义如下:

import axios,{ AxiosInstance }from'axios'// 从环境变量中读取后端API地址constBACKEND_API_URL=`${import.meta.env.VITE_BASE_URL}`// 创建axios实例constservice: AxiosInstance = axios.create({// api 的 后端地址baseURL:BACKEND_API_URL,// 请求超时时间timeout:300000})

我们将请求超时时间配置为 5 分钟(300000 毫秒),这一设置的核心考量是适配企业级应用的业务特性 —— 部分场景下后端需处理耗时较长的任务(例如导出近一个月的业务数据并生成 Excel 文件);但需注意,若为高访问量的互联网应用,建议将超时阈值控制在 10 秒以内,以兼顾系统的响应效率和资源利用率。

配置请求拦截器

全局请求拦截器主要承担两项核心处理逻辑:

  1. 认证令牌统一注入:从本地存储(如 localStorage/sessionStorage)中读取前后端统一的认证令牌(token),并将其注入到所有请求的 HTTP 请求头中,后端可通过解析该令牌完成接口的身份鉴权与权限校验。
  2. 请求参数规范化处理
    • 针对 POST 请求:若请求的 Content-Type 为application/x-www-form-urlencoded,会通过 qs 工具类将请求体(data)中的 JSON 对象转换为键值对形式的字符串(各键值对以 & 分隔),适配该 Content-Type 的参数传输规范;
    • 针对 GET 请求:尽管 axios 默认会将 params 属性中的 JSON 对象转换为以 & 分隔的查询字符串,但我们额外增加了自定义 URL 编码逻辑 —— 这一步至关重要,因为业务数据中常包含空格、&、? 等与 URL 语法冲突的特殊字符,若未编码直接传输,极易导致参数解析异常、数据不完整甚至请求失败。

源码如下:

// requContent-Type拦截器 service.interceptors.request.use((config: InternalAxiosRequestConfig)=>{// 读取tokenconst token =getToken()// 附加token到header中if(token){ config.headers['X-Token']= token }if(config.method ==='post'){// post请求 参数编码if((config.headers as AxiosRequestHeaders)['Content-Type']==='application/x-www-form-urlencoded'){ config.data = qs.stringify(config.data)}}elseif(config.method ==='get'){// get请求参数编码if(config.params){let url = config.url as string url +='?'const keys = Object.keys(config.params)for(const key of keys){if(config.params[key]!==void0&& config.params[key]!==null){ url +=`${key}=${encodeURIComponent(config.params[key])}&`}} url = url.substring(0, url.length -1) config.url = url config.params ={}}}return config },(error: AxiosError)=>{// 控制台输出日志 console.log(error) Promise.reject(error)})

配置响应拦截器

响应拦截器里主要是处理服务器端返回的各种情况,处理逻辑如下:

在这里插入图片描述

对应源码如下:

// response 拦截器 service.interceptors.response.use((response: AxiosResponse<any>)=>{if(response.config.responseType ==='blob'){// 如果是文件流,直接返回return response }// 处理所有2xx成功响应if(response.status >=REQUEST_SUCCESS&& response.status <300){return response }// 非2xx响应应该进入错误拦截器,构造AxiosError兼容对象const axiosError =newError(`HTTP ${response.status}: ${response.statusText}`)as any axiosError.response = response axiosError.isAxiosError =true axiosError.config = response.config throw axiosError },(error: AxiosError<{ message?: string }>)=>{if(!error.response){ ElMessage.error('请求远程服务器失败')return Promise.reject(error)}const{ status, data }= error.response switch(status){caseUNAUTHORIZED:// 收到401响应时,给出友好提示 ElMessage.warning('未登录或会话超时,请重新登录')// 清空浏览器缓存 wsCache.clear()// 执行页面刷新setTimeout(()=>{ location.reload()},2000)breakcaseNOT_FOUND: ElMessage.error('未找到服务,请确认')breakcaseMETHOD_NOT_ALLOWED: ElMessage.error('请求的方法不支持,请确认')breakdefault:if(data?.message){ ElMessage.error(data.message)}else{ ElMessage.error('请求远程服务器失败')}break}return Promise.reject(error)})

封装前后端交互

在前文说的 service 的基础上,我们做一个封装,提供了统一的HTTP请求方法和消息提示处理机制。

  • 接收请求配置参数:url、method、params、data等
  • 设置默认请求头 Content-Type 为 application/json
  • 调用底层service(axios实例)发起请求

源码如下:

import{ service }from'./service'import{ ElMessage }from'element-plus'import{ getToken }from'@/utils/auth'// 基础请求函数constrequest=(option: any)=>{const{ url, method, params, data, headersType, responseType }= option returnservice({url: url, method, params, data,responseType: responseType,headers:{'Content-Type': headersType ||'application/json'}})}// 处理显示信息constprocessShowInfo=(option: any,res: any,method: string)=>{// 对于GET请求,默认不显示提示;其他请求默认显示提示const shouldShowInfo =(()=>{// 如果明确设置了showInfo,则按照设置if(option.params?.showInfo !==undefined){return option.params.showInfo }// 默认行为:GET请求不显示,其他请求显示return method.toLowerCase()!=='get'})()if(shouldShowInfo && res?.data?.message){ ElMessage.info(res.data.message)}}// 通用的请求封装函数constcreateRequest=(method: string)=>{return(option: any): Promise<any>=>{returnnewPromise((resolve, reject)=>{request({ method,...option }).then((res: any)=>{processShowInfo(option, res, method)resolve(res.data)}).catch((err: any)=>{reject(err)})})}}constupload=(option: any)=>{ option.headersType ='multipart/form-data'returncreateRequest('post')(option)}// 下载文件constdownload=(option: any)=>{const baseUrl =import.meta.env.VITE_BASE_URLlet url = baseUrl + option.url const params = option.params ||{}// 构建URL参数const urlParams =newURLSearchParams(params).toString()if(urlParams){ url +='?'+ urlParams }// 添加tokenconst token =getToken()const separator = url.includes('?')?'&':'?' url += separator +'X-Token='+encodeURIComponent(token)// 创建临时下载链接const link = document.createElement('a') link.href = url link.download =''// 让浏览器自动处理文件名 link.style.display ='none' document.body.appendChild(link) link.click() document.body.removeChild(link)}exportdefault{get:createRequest('get'),post:createRequest('post'),delete:createRequest('delete'),put:createRequest('put'), upload, download }

我们对 HTTP 标准请求方法(GET、POST、PUT、DELETE)进行了统一封装,并在请求配置项(options)中新增自定义属性showInfo,用于灵活控制请求完成后的用户提示逻辑:

● 默认规则:查询类请求(如用户点击菜单加载数据列表的 GET 请求),通常无需向用户展示 “响应成功” 的提示,因此 GET 请求的showInfo默认值为false;而增删改等数据操作类请求(POST/PUT/DELETE),为保障用户感知,默认展示操作结果提示(showInfo默认值为true)。

● 特殊场景适配:若需覆盖默认行为,可显式指定showInfo的值。例如,用户点击查看单条新闻时,系统会自动发起 PUT 请求更新该新闻的阅读次数,此类隐性操作无需向用户弹窗提示,只需将showInfo设为false即可修改默认行为。

针对文件上传场景,尽管其底层仍基于 POST 请求实现,但需将请求的 Content-Type 设置为multipart/form-data(适配文件流的传输规范),因此我们将文件上传逻辑单独封装为独立方法,简化调用方的使用成本。

文件下载的实现逻辑更为特殊:需动态创建 HTML 的a标签,将标签的href属性指向文件下载接口地址,同时将认证 token 作为 URL 参数附加到地址中完成身份鉴权,最后触发a标签的点击事件触发下载。基于这一特殊逻辑,我们也将文件下载封装为独立方法,统一处理鉴权与下载触发逻辑。

通过上述统一封装,我们将 HTTP 请求的公共逻辑(如超时配置、参数规范化、认证令牌注入、操作提示控制等)进行集中配置与管理,极大简化了业务层的接口调用逻辑,既减少了重复代码的编写,也提升了接口调用的规范性和整体可维护性。

业务功能具体调用示例如下:

exportconstCOMMON_METHOD={serveUrl:'',init(){return request.get({url:this.serveUrl +'init'})},get(id){return request.get({url:this.serveUrl + id })},add(params){return request.post({url:this.serveUrl,data: params })},modify(params){return request.put({url:this.serveUrl,data: params })},remove(id){return request.delete({url:this.serveUrl + id })},page(params){return request.get({url:this.serveUrl +'page', params })},list(params){return request.get({url:this.serveUrl +'list', params })},// 批量复制新增addByCopy(ids){return request.post({url:this.serveUrl + ids })},// 单条复制新增addSingleByCopy(id){return request.post({url:this.serveUrl + id +'/addSingleByCopy'})}}// 组织机构exportconst organization = Object.assign({},COMMON_METHOD,{serveUrl:'/'+ moduleName +'/'+'organization'+'/',tree(){return request.get({url:this.serveUrl +'tree'})},cascader(){return request.get({url:this.serveUrl +'cascader'})},enable(id){return request.put({url:this.serveUrl + id +'/enable'})},disable(id){return request.put({url:this.serveUrl + id +'/disable'})},// 下载导入模板downloadImportTemplate(){return request.download({url:this.serveUrl +'downloadImportTemplate'})},// 导入import(formData){return request.upload({url:this.serveUrl +'importExcel',data: formData })},// 导出export(params){return request.download({url:this.serveUrl +'exportExcel', params })}})

基本就是 get 请求参数放 params 属性中,put 和 delete 请求参数放在 data 属性中。

六、总结

Axios作为前端数据请求的利器,以其简洁易用的API、强大的功能和广泛的适用性,在前端开发中扮演着重要角色。无论是简单的数据获取,还是复杂的表单提交和文件上传,Axios都能轻松应对。当然,在使用过程中可能会遇到一些问题,但只要掌握好相关的解决办法,就能充分发挥Axios的优势,让前端开发更加高效、顺畅。希望大家都能熟练运用Axios,打造出优秀的前端应用!

Read more

OpenWebUI联网搜索实战:如何用SearXNG让本地大模型获取实时信息(附百度/360配置)

OpenWebUI联网搜索实战:如何用SearXNG让本地大模型获取实时信息(附百度/360配置) 如果你在本地运行大模型,比如用Ollama部署了Qwen、Llama或者DeepSeek,可能会发现一个尴尬的问题:模型的知识截止日期是固定的,它不知道今天股市涨跌,不清楚最新的科技新闻,甚至不知道明天是什么节日。这种“信息孤岛”的感觉,让本地大模型的实用性大打折扣。 我最初搭建OpenWebUI环境时,也遇到了这个痛点。看着模型一本正经地分析过时的数据,那种无力感让我开始寻找解决方案。市面上有不少联网搜索方案,但要么配置复杂,要么对国内网络环境不友好。经过几周的折腾和测试,我发现SearXNG这个开源元搜索引擎,配合OpenWebUI的联网搜索功能,是目前最稳定、最灵活的方案之一。 更重要的是,通过合理配置SearXNG,我们可以让本地大模型直接调用百度、360等国内搜索引擎,获取符合中文用户习惯的实时信息。这不仅仅是技术上的连接,更是让本地AI真正“接地气”的关键一步。下面我就把自己踩过的坑、验证过的配置,以及实际效果对比,毫无保留地分享给你。 1. 为什么需要SearXN

By Ne0inhk
实战:手写一个通用Web层鉴权注解,解决水平权限漏洞

实战:手写一个通用Web层鉴权注解,解决水平权限漏洞

实战:手写一个通用Web层鉴权注解,解决水平权限漏洞 * 一、背景:一次渗透测试引发的改造 * 二、需求分析:如何高效修复 * 三、业务模型:用户-公司授权关系 * 四、整体架构设计 * 五、代码实现:一步一步来 * 5.1 注解定义 * 5.2 权限管理服务 * 5.3 AOP切面:核心逻辑 * 六、使用示例 * 6.1 场景1:最简单的用法 * 6.2 场景2:对象属性 * 6.3 场景3:批量操作 * 6.4 场景4:嵌套属性 * 6.5 场景5:类级别默认配置 * 七、

By Ne0inhk
cann-recipes-train 仓库深度解读:昇腾平台下 DeepSeek-R1 与 Qwen2.5 强化学习训练优化实践

cann-recipes-train 仓库深度解读:昇腾平台下 DeepSeek-R1 与 Qwen2.5 强化学习训练优化实践

cann-recipes-train 仓库深度解读:昇腾平台下 DeepSeek-R1 与 Qwen2.5 强化学习训练优化实践 前言 自 DeepSeek-R1 发布以来,大模型的强化学习(RL)训练掀起了新一轮的技术热潮。各大厂商与开源社区纷纷投入实践,持续探索更高效的 RL 训练体系。本文将基于 cann-recipes-train 仓库,解读两个实践样例:DeepSeek-R1 的 RL 训练优化实践样例、基于 verl 框架的 Qwen2.5 强化学习实践样例 cann-recipes-train 仓库全景解析:昇腾训练优化的"实战底座" 大模型训练拼效率的阶段,CANN 直接帮我们搞定了底层异构硬件适配、资源调度这些麻烦事,不用再从零研究 GPU 和 NPU 怎么协同,现有模型代码也不用大改就能对接,训

By Ne0inhk

WebToEpub实战指南:三步将网页小说变成随身电子书

WebToEpub实战指南:三步将网页小说变成随身电子书 【免费下载链接】WebToEpubA simple Chrome (and Firefox) Extension that converts Web Novels (and other web pages) into an EPUB. 项目地址: https://gitcode.com/gh_mirrors/we/WebToEpub 你是否曾经遇到过这样的情况:找到了一部精彩的网络小说,却因为网络不稳定无法畅快阅读?或者想要在通勤路上看小说,却担心流量消耗?今天,我将带你掌握WebToEpub这个神器,三步搞定网页转EPUB,让你的阅读体验从此无拘无束! 🔍 痛点分析:为什么你需要WebToEpub? 传统阅读方式的三大困扰: 1. 网络依赖强 - 必须在线才能阅读,断网就"剧终" 2. 阅读体验差

By Ne0inhk