08. Vue封装axios并实现前后端接口联调
安装 axios 插件
npm i axios -S 后端配置跨域
from starlette.middleware.cors import CORSMiddleware
origins = [
"http://localhost:5173",
"http://127.0.0.1:5173",
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins, # 允许的前端源,不要直接写 ["*"]
allow_credentials=True, # ✅ 关键:允许前端携带 Authorization token
allow_methods=["*"], # 允许所有请求方法 GET POST PUT DELETE OPTIONS
allow_headers=["*"], # 允许所有请求头(包含Authorization)
)前端的跨域处理方案
server: {
port: 5173,
// 开发代理,前端跨域配置
proxy: {
'/api': {
target: 'http://127.0.0.1:8000', // 后端地址
changeOrigin: true, // 修改源,核心
}
}
}配置环境变量
VITE_API_BASE_URL=http://127.0.0.1:8000存取 token、user 的工具方法
utils/auth.js
// 存储token、存储用户信息、获取token、用户信息,删除token、用户信息这些作用
const TOKEN_KEY = 'token'
const USER_KEY = 'userInfo'
export function getToken() {
return localStorage.getItem(TOKEN_KEY)
}
export function setToken(token) {
localStorage.setItem(TOKEN_KEY, token)
}
export function removeToken() {
localStorage.removeItem(TOKEN_KEY)
}
export function getUserInfo() {
const str = localStorage.getItem(USER_KEY)
return str ? JSON.parse(str) : null
}
export function setUserInfo(userInfo) {
localStorage.setItem(USER_KEY, JSON.stringify(userInfo))
}
export function removeUserInfo() {
localStorage.removeItem(USER_KEY)
}
// 登出
export function logout() {
removeToken()
removeUserInfo()
}
utils/user.js
// 仅供页面 setup 函数使用
import { ref } from 'vue'
import { getUserInfo, setToken, setUserInfo } from './auth'
const userInfo = ref(getUserInfo()) // 响应式对象 可以随时更新最新的值
export function useUser() {
function saveLoginData(data) {
setToken(data.token)
setUserInfo(data.user)
userInfo.value = data.user
}
function updateUser(user) {
setUserInfo(user)
userInfo.value = data.user
}
function reloadUser() {
userInfo.value = getUserInfo()
}
return {
userInfo,
saveLoginData,
updateUser,
reloadUser
}
}
utils/request.js
import axios from 'axios'
import { getToken, logout } from './auth'
import { ElMessage } from 'element-plus'
import router from '@/router'
const service = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
timeout: 30000 //30s
})
// 请求拦截器的配置
service.interceptors.request.use(
(config) => {
const token = getToken()
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
},
(err) => Promise.reject(err)
)
// 返回的拦截器配置
service.interceptors.response.use(
(res) => {
const data = res.data
if (data.code !== 200) {
ElMessage.error(data.message || '请求失败')
if (data.code === 401) {
logout()
router.push('/login')
return Promise.reject(data)
}
}
return data
},
(err) => {
if (err.response) {
const httpStatusCode = err.response.status
if (httpStatusCode === 401) {
ElMessage.error('登录状态已失效,请重新登录')
logout()
router.push('/login')
} else {
ElMessage.error(err.response.data?.message || '请求失败')
}
} else {
// 后端没启动,网络异常了
ElMessage.error('网络异常,请检查后端服务')
}
return Promise.reject(err)
}
)
export default service
发送 api 请求
api/auth.js
import request from '@/utils/request'
/**
* 登录请求
*/
export function loginApi(data) {
return request({
url: '/api/auth/login',
method: 'post',
data
})
}
登录页面发起请求
<script setup>
import { reactive, ref } from 'vue'
import { User, Lock } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import router from '@/router'
import { loginApi } from '@/api/auth'
import { useUser } from '@/utils/user'
const { saveLoginData } = useUser()
const form = reactive({
username: '',
password: ''
})
const loadingValue = ref(false)
const login = async () => {
loadingValue.value = true
const res = await loginApi(form)
loadingValue.value = false
if (res.code === 200) {
saveLoginData(res.data)
ElMessage.success('登录成功')
router.push('/manager/home')
}
}
</script>页面获取用户信息
<template>
<div>欢迎您:{{ userInfo?.username }}!</div>
</template>
<script setup>
import { useUser } from '@/utils/user'
const { userInfo } = useUser()
</script>路由守卫 router/index.js
import { getToken } from '@/utils/auth'
// 路由守卫:验证token
router.beforeEach((to, from) => {
const token = getToken()
if (to.path === '/login' || to.path === '/register') {
return true
} else {
return token ? true : '/login'
}
})