FastAPI + LangGraph 从零开发智能实验室预约系统

本集视频在 B 站 BV13hbP6zEUd · P9

去 B 站看本集

08. Vue封装axios并实现前后端接口联调

安装 axios 插件

bash
npm i axios -S  

后端配置跨域

python
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)
)

前端的跨域处理方案

vue
server: {
    port: 5173,
    // 开发代理,前端跨域配置
    proxy: {
      '/api': {
        target: 'http://127.0.0.1:8000', // 后端地址
        changeOrigin: true, // 修改源,核心
      }
    }
  }

配置环境变量

javascript
VITE_API_BASE_URL=http://127.0.0.1:8000

存取 token、user 的工具方法

utils/auth.js

javascript
//  存储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

javascript
// 仅供页面  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

javascript
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

javascript
import request from '@/utils/request'

/**
 * 登录请求
 */
export function loginApi(data) {
  return request({
    url: '/api/auth/login',
    method: 'post',
    data
  })
}

登录页面发起请求

vue
<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>

页面获取用户信息

vue
<template>
  <div>欢迎您:{{ userInfo?.username }}!</div>
</template>

<script setup>
  import { useUser } from '@/utils/user'
  const { userInfo } = useUser()
</script>

路由守卫 router/index.js

javascript
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'
  }
})