1
This commit is contained in:
387
demo-project/未登录接口调用问题修复.md
Normal file
387
demo-project/未登录接口调用问题修复.md
Normal file
@@ -0,0 +1,387 @@
|
||||
# 未登录接口调用问题修复
|
||||
|
||||
## 🔍 问题描述
|
||||
|
||||
### 错误现象
|
||||
|
||||
在用户未登录时,`sys_parameter/key` 接口被调用,因为没有 token 导致接口报错。
|
||||
|
||||
### 错误原因
|
||||
|
||||
在 `demo-project/src/main.js` 的 `mounted` 钩子中,无条件调用了 `getSysTitle` action:
|
||||
|
||||
```javascript
|
||||
mounted() {
|
||||
AdminFramework.uiTool.setRem()
|
||||
|
||||
// ❌ 无论是否登录都调用
|
||||
this.$store.dispatch('app/getSysTitle', {
|
||||
defaultTitle: 'Demo 管理系统',
|
||||
defaultLogo: ''
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
`getSysTitle` action 会调用后端接口:
|
||||
- `paramSetupServer.getOne('sys_title')` - 获取系统标题
|
||||
- `paramSetupServer.getOne('sys_logo')` - 获取系统 Logo
|
||||
|
||||
这些接口需要 token 认证,但在应用启动时用户可能还没登录,导致接口调用失败。
|
||||
|
||||
### 调用时机问题
|
||||
|
||||
```
|
||||
应用启动
|
||||
↓
|
||||
Vue 实例 mounted
|
||||
↓
|
||||
调用 getSysTitle ← ❌ 此时可能未登录
|
||||
↓
|
||||
调用后端接口
|
||||
↓
|
||||
没有 token,接口报错 ❌
|
||||
```
|
||||
|
||||
## ✅ 解决方案
|
||||
|
||||
### 方案:只在已登录时调用接口
|
||||
|
||||
**核心思路**:
|
||||
1. 在调用 `getSysTitle` 前检查是否已登录(是否有 token)
|
||||
2. 已登录:调用接口获取系统标题
|
||||
3. 未登录:直接使用默认标题
|
||||
|
||||
### 修改 1:`demo-project/src/main.js`
|
||||
|
||||
**修改前**:
|
||||
```javascript
|
||||
mounted() {
|
||||
AdminFramework.uiTool.setRem()
|
||||
|
||||
// ❌ 无条件调用,可能导致未登录时接口报错
|
||||
this.$store.dispatch('app/getSysTitle', {
|
||||
defaultTitle: 'Demo 管理系统',
|
||||
defaultLogo: ''
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
**修改后**:
|
||||
```javascript
|
||||
mounted() {
|
||||
AdminFramework.uiTool.setRem()
|
||||
|
||||
// ✅ 检查是否已登录
|
||||
const token = this.$store.state.user.token
|
||||
if (token) {
|
||||
// 已登录,调用接口获取系统标题
|
||||
this.$store.dispatch('app/getSysTitle', {
|
||||
defaultTitle: 'Demo 管理系统',
|
||||
defaultLogo: ''
|
||||
})
|
||||
} else {
|
||||
// 未登录,直接设置默认标题
|
||||
document.title = 'Demo 管理系统'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 修改 2:`src/store/app.js` 的 `getSysTitle` action
|
||||
|
||||
**修改前**:
|
||||
```javascript
|
||||
async getSysTitle({ state, commit }, { defaultTitle = '智能代码平台', defaultLogo = '' }) {
|
||||
let formModel = {
|
||||
title: defaultTitle,
|
||||
logoUrl: defaultLogo
|
||||
}
|
||||
|
||||
try {
|
||||
// ❌ 直接调用接口,不检查是否已登录
|
||||
let res1 = await paramSetupServer.getOne('sys_title')
|
||||
if (res1 && res1.data) {
|
||||
formModel.title = res1.data.value
|
||||
document.title = res1.data.value
|
||||
}
|
||||
let res2 = await paramSetupServer.getOne('sys_logo')
|
||||
if (res2 && res2.data) {
|
||||
formModel.logoUrl = res2.data.value
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('获取系统标题失败,使用默认标题:', error || '接口调用失败')
|
||||
document.title = formModel.title
|
||||
}
|
||||
|
||||
commit('setSysTitle', formModel)
|
||||
}
|
||||
```
|
||||
|
||||
**修改后**:
|
||||
```javascript
|
||||
async getSysTitle({ state, commit, rootState }, { defaultTitle = '智能代码平台', defaultLogo = '' }) {
|
||||
let formModel = {
|
||||
title: defaultTitle,
|
||||
logoUrl: defaultLogo
|
||||
}
|
||||
|
||||
// ✅ 检查是否已登录(有 token)
|
||||
const token = rootState.user.token
|
||||
|
||||
if (token) {
|
||||
// 已登录,尝试从后端获取系统标题
|
||||
try {
|
||||
let res1 = await paramSetupServer.getOne('sys_title')
|
||||
if (res1 && res1.data) {
|
||||
formModel.title = res1.data.value
|
||||
document.title = res1.data.value
|
||||
}
|
||||
let res2 = await paramSetupServer.getOne('sys_logo')
|
||||
if (res2 && res2.data) {
|
||||
formModel.logoUrl = res2.data.value
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('获取系统标题失败,使用默认标题:', error || '接口调用失败')
|
||||
document.title = formModel.title
|
||||
}
|
||||
} else {
|
||||
// 未登录,直接使用默认标题
|
||||
document.title = formModel.title
|
||||
}
|
||||
|
||||
commit('setSysTitle', formModel)
|
||||
}
|
||||
```
|
||||
|
||||
## 📊 修复后的流程
|
||||
|
||||
### 未登录时
|
||||
|
||||
```
|
||||
应用启动
|
||||
↓
|
||||
Vue 实例 mounted
|
||||
↓
|
||||
检查 token
|
||||
↓
|
||||
token 不存在 ✅
|
||||
↓
|
||||
直接设置默认标题 "Demo 管理系统"
|
||||
↓
|
||||
不调用后端接口 ✅
|
||||
```
|
||||
|
||||
### 已登录时
|
||||
|
||||
```
|
||||
应用启动
|
||||
↓
|
||||
Vue 实例 mounted
|
||||
↓
|
||||
检查 token
|
||||
↓
|
||||
token 存在 ✅
|
||||
↓
|
||||
调用 getSysTitle action
|
||||
↓
|
||||
检查 rootState.user.token
|
||||
↓
|
||||
token 存在 ✅
|
||||
↓
|
||||
调用后端接口获取系统标题
|
||||
↓
|
||||
成功:使用后端返回的标题
|
||||
失败:使用默认标题
|
||||
```
|
||||
|
||||
### 登录流程
|
||||
|
||||
```
|
||||
用户登录
|
||||
↓
|
||||
保存 token 到 store
|
||||
↓
|
||||
刷新页面
|
||||
↓
|
||||
应用重新启动
|
||||
↓
|
||||
Vue 实例 mounted
|
||||
↓
|
||||
检查 token(此时有 token)✅
|
||||
↓
|
||||
调用 getSysTitle 获取系统标题
|
||||
```
|
||||
|
||||
## 🎯 优化点
|
||||
|
||||
### 1. 双重检查
|
||||
|
||||
**第一层检查**(`main.js`):
|
||||
```javascript
|
||||
const token = this.$store.state.user.token
|
||||
if (token) {
|
||||
this.$store.dispatch('app/getSysTitle', ...)
|
||||
}
|
||||
```
|
||||
|
||||
**第二层检查**(`app.js`):
|
||||
```javascript
|
||||
const token = rootState.user.token
|
||||
if (token) {
|
||||
// 调用接口
|
||||
}
|
||||
```
|
||||
|
||||
**为什么需要双重检查?**
|
||||
- 第一层:避免不必要的 action 调用
|
||||
- 第二层:防止 action 被其他地方调用时没有检查 token
|
||||
|
||||
### 2. 默认标题处理
|
||||
|
||||
**未登录时**:
|
||||
```javascript
|
||||
document.title = 'Demo 管理系统'
|
||||
```
|
||||
|
||||
**已登录但接口失败时**:
|
||||
```javascript
|
||||
document.title = formModel.title // 使用传入的 defaultTitle
|
||||
```
|
||||
|
||||
**已登录且接口成功时**:
|
||||
```javascript
|
||||
document.title = res1.data.value // 使用后端返回的标题
|
||||
```
|
||||
|
||||
### 3. 错误处理
|
||||
|
||||
```javascript
|
||||
try {
|
||||
// 调用接口
|
||||
} catch (error) {
|
||||
console.warn('获取系统标题失败,使用默认标题:', error || '接口调用失败')
|
||||
document.title = formModel.title
|
||||
}
|
||||
```
|
||||
|
||||
即使接口调用失败,也会优雅降级到默认标题,不影响用户使用。
|
||||
|
||||
## ✅ 验证清单
|
||||
|
||||
### 未登录场景
|
||||
|
||||
- ✅ 访问首页显示登录页面
|
||||
- ✅ 页面标题显示 "Demo 管理系统"
|
||||
- ✅ 控制台没有接口报错
|
||||
- ✅ Network 标签中没有 `sys_parameter/key` 请求
|
||||
|
||||
### 已登录场景
|
||||
|
||||
- ✅ 登录成功后刷新页面
|
||||
- ✅ 页面标题显示系统标题(如果后端有配置)
|
||||
- ✅ 如果后端接口失败,显示默认标题
|
||||
- ✅ 系统 Logo 正确显示(如果后端有配置)
|
||||
|
||||
### 登录流程
|
||||
|
||||
- ✅ 输入用户名密码
|
||||
- ✅ 点击登录
|
||||
- ✅ 登录成功
|
||||
- ✅ 页面刷新
|
||||
- ✅ 进入系统首页
|
||||
- ✅ 系统标题正确显示
|
||||
|
||||
## 💡 最佳实践
|
||||
|
||||
### 1. 接口调用前检查认证状态
|
||||
|
||||
```javascript
|
||||
// ✅ 好的做法
|
||||
if (token) {
|
||||
await api.getData()
|
||||
}
|
||||
|
||||
// ❌ 不好的做法
|
||||
try {
|
||||
await api.getData() // 可能因为没有 token 而失败
|
||||
} catch (error) {
|
||||
// 处理错误
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 提供默认值
|
||||
|
||||
```javascript
|
||||
// ✅ 好的做法
|
||||
const title = res.data?.value || defaultTitle
|
||||
|
||||
// ❌ 不好的做法
|
||||
const title = res.data.value // 可能是 undefined
|
||||
```
|
||||
|
||||
### 3. 优雅降级
|
||||
|
||||
```javascript
|
||||
// ✅ 好的做法
|
||||
if (token) {
|
||||
try {
|
||||
const data = await api.getData()
|
||||
useData(data)
|
||||
} catch (error) {
|
||||
useDefaultData()
|
||||
}
|
||||
} else {
|
||||
useDefaultData()
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 避免不必要的请求
|
||||
|
||||
```javascript
|
||||
// ✅ 好的做法
|
||||
if (needData && token) {
|
||||
await api.getData()
|
||||
}
|
||||
|
||||
// ❌ 不好的做法
|
||||
await api.getData() // 无论是否需要都请求
|
||||
```
|
||||
|
||||
## 🔧 相关修改
|
||||
|
||||
### 其他可能需要检查的地方
|
||||
|
||||
如果项目中还有其他在应用启动时调用的接口,也需要检查是否需要 token:
|
||||
|
||||
1. **检查 `main.js` 的 `mounted` 钩子**
|
||||
- 确保所有接口调用都检查了 token
|
||||
|
||||
2. **检查 `App.vue` 的 `mounted` 钩子**
|
||||
- 确保没有未登录时调用需要认证的接口
|
||||
|
||||
3. **检查路由守卫**
|
||||
- 确保路由守卫正确处理未登录的情况
|
||||
|
||||
4. **检查 Vuex actions**
|
||||
- 确保需要认证的 actions 都检查了 token
|
||||
|
||||
## 📝 总结
|
||||
|
||||
### 修复的问题
|
||||
|
||||
- ✅ 未登录时不再调用需要认证的接口
|
||||
- ✅ 避免了接口报错
|
||||
- ✅ 提供了优雅的降级方案
|
||||
- ✅ 改善了用户体验
|
||||
|
||||
### 代码改进
|
||||
|
||||
- **更健壮**:双重检查确保不会在未登录时调用接口
|
||||
- **更友好**:即使接口失败也能正常显示默认标题
|
||||
- **更清晰**:代码逻辑更容易理解和维护
|
||||
|
||||
---
|
||||
|
||||
**未登录接口调用问题已修复!** 🎉
|
||||
|
||||
现在系统会智能判断是否已登录,只在必要时调用后端接口。
|
||||
|
||||
Reference in New Issue
Block a user