Mobile wallpaper 1Mobile wallpaper 2Mobile wallpaper 3Mobile wallpaper 4Mobile wallpaper 5Mobile wallpaper 6
1103 字
6 分钟
5-与之对应的前端-Springboot Vue 教程

响应拦截器#

既然后端有统一响应格式,那么前端也应该有与之对应的部分,那就是响应拦截器。

响应拦截器 (Response Interceptor) 是 HTTP 客户端库(例如 Axios、Fetch API 的封装等)提供的一种机制,它允许你在 HTTP 响应到达你的应用程序代码之前,对其进行统一的处理或修改

可以把它想象成一个“门卫”或者“处理器”,所有从服务器返回的 HTTP 响应,在被你的 then()await 接收到之前,都要先经过它。

Result 接口#

在编写响应拦截器之前,先写一个Result接口

在src中创建一个interface文件夹,然后在interface中创建一个index.ts

export interface Result<T = any> {
code: number,
msg: string,
data?: T
}

这是一个接口,和后端的统一响应格式对应。

响应拦截器#

我们来到前端的Api下的index.ts, 添加如下代码:

api.interceptors.response.use(
response => {
const res: Result = response.data
// 如果 code 不是 200,表示业务失败
if (res.code !== 200) {
console.error(`后端错误: ${res.msg}`);
// 返回一个被拒绝的 Promise,携带自定义的错误信息
return Promise.reject(new Error(res.msg))
} else {
return res.data
}
},
error => {
// 处理 HTTP 状态码层面的错误 (例如 404, 500 等)
console.error('HTTP 请求错误:', error)
return Promise.reject(error)
}
)

LoginResponse接口#

后端将数据都封装在了LoginResponseDto里,那么前端也要有对应的LoginResponse接口,

我们在上文创建的interface文件夹中,创建一个User.ts 代码如下:

interface User {
id: number,
username: string,
}
export interface saToken {
tokenName: string,
tokenValue: string,
isLogin: boolean,
loginId: string,
loginType: string,
tokenTimeout: number,
sessionTimeout: number,
tokenSessionTimeout: number,
tokenActiveTimeout: number,
loginDeviceType: string,
tag?: string
}
interface LoginResponse {
user: User,
saToken: saToken
}
export type {
User,
LoginResponse
}

接下来修改一下登录的api,打开api文件夹下的Api.ts 修改登录api,代码如下:

function login(username: string, password: string) {
return api.post<Result<any>, LoginResponse>('/user/login', {
username,
password
})
}

在post方法后添加泛型Result<any>LoginResponse

它表示 api.post 这个 Axios 请求,从后端接收到的原始数据结构(也就是 response.data)将会是 Result 类型,并且这个 Resultdata 字段里包含的实际数据类型是 LoginResponse。 这样写可以告诉编译器和阅读代码的人,这个部分是用来处理什么类型的数据的。 编译器也会给出相应的补全,在后续编写代码时。

完善前端登录注册逻辑#

之前只简单模拟了一下登录注册,现在开始正式完善。

登录#

打开你的Login.vue

更改你的登录方法handleLogin

const handleLogin = async () => {
try {
if (!loginFormRef.value) {
ElMessage.error("表单引用未就绪")
return
}
// 验证表单
const valid = await loginFormRef.value.validate()
if (!valid) return
loading.value = true
// 这里替换为实际的登录API调用
const loginResult = await login(loginForm.username, loginForm.password)
const user = loginResult.user
const saToken = loginResult.saToken
localStorage.setItem('curUser', JSON.stringify(user))
localStorage.setItem('token', JSON.stringify(saToken))
ElMessage.success("登录成功")
router.push('/')
} catch (error: any) {
ElMessage.error(error.message || '登录失败')
} finally {
loading.value = false
}
}

注册#

打开你的Register.vue

更改你的注册方法handleRegister

const handleRegister = async () => {
try {
if (!registerFormRef.value) {
ElMessage.error("表单引用未就绪")
return
}
// 验证表单
const valid = await registerFormRef.value.validate()
if (!valid) return
loading.value = true
await register(registerForm.username, registerForm.password)
router.push('/login')
} catch (error: any) {
ElMessage.error(error.message || '注册失败')
} finally {
loading.value = false
}
}

流程图#

graph TD subgraph User Interaction A[用户操作] --> B(点击登录/注册/创建文章等) end subgraph Vue Application B --> C{调用API服务函数} C -->|发起 HTTP 请求| D(Axios 实例) end subgraph Axios Request Interceptor D --> E{请求拦截器} E -->|获取 Token| F{判断是否存在 Token} F --是--> G(将 Token 加入请求头) G --> H[请求发送] F --否--> H end subgraph Backend API H --> I(后端 API 接口) end subgraph Axios Response Interceptor I --> J(后端返回响应) J --> K{响应拦截器} K --> L{判断响应 Code 是否为 200} L --否--> M[统一错误处理/提示] M --> N(Promise.reject Error) L --是--> O{提取响应数据} O -->|返回 res.data| P(Promise.resolve DTO) end subgraph Vue Application Response N --> Q[API服务函数捕获错误] P --> R[API服务函数接收数据] Q --> S(组件/页面显示错误信息) R --> T(组件/页面更新数据/状态) end style A fill:#e0f7fa,stroke:#00bcd4,stroke-width:2px style B fill:#e8f5e9,stroke:#4caf50,stroke-width:2px style C fill:#fffde7,stroke:#ffeb3b,stroke-width:1px style D fill:#e0f2f7,stroke:#007bff,stroke-width:1px style E fill:#fff3e0,stroke:#ff9800,stroke-width:1px style F fill:#ffebee,stroke:#f44336,stroke-width:1px style G fill:#f8bbd0,stroke:#e91e63,stroke-width:1px style H fill:#f0f4c3,stroke:#cddc39,stroke-width:1px style I fill:#fbe9e7,stroke:#ff5722,stroke-width:1px style J fill:#fff9c4,stroke:#ffc107,stroke-width:1px style K fill:#bbdefb,stroke:#2196f3,stroke-width:1px style L fill:#90caf9,stroke:#2196f3,stroke-width:1px style M fill:#b3e5fc,stroke:#03a9c4,stroke-width:1px style N fill:#e1f5fe,stroke:#03a9f4,stroke-width:1px style O fill:#c8e6c9,stroke:#81c784,stroke-width:1px style P fill:#a5d6a7,stroke:#66bb6a,stroke-width:1px style Q fill:#ffcdd2,stroke:#ef9a9a,stroke-width:1px style R fill:#dcedc8,stroke:#aed581,stroke-width:1px style S fill:#ffecb3,stroke:#ffe082,stroke-width:1px style T fill:#c5e1a5,stroke:#9ccc65,stroke-width:1px
分享

如果这篇文章对你有帮助,欢迎分享给更多人!

5-与之对应的前端-Springboot Vue 教程
https://blog.yumui.top/posts/5-springboot-vue/
作者
Yu Felix
发布于
2025-05-10
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时