代码地址:GitHub - wendingming/fastapi-vue-postgresql: fastapi+vue+postgresql搭建项目
昨天经过实测,发现http.js的响应拦截存在问题,
昨天的代码:
//响应拦截
http.interceptors.response.use(res => {
//这里是错误的,res的响应status,不是在成功响应判断,而应该在失败响应里面判断
const code = res.data.status_code || 200;
const msg = errorCode[code] || res.data.detail || errorCode['default']
if (code === 401) {
ElMessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', {
confirmButtonText: '重新登录',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
store.dispatch('loginOut').then(() => {
location.href = '/index';
})
})
} else if (code === 500) {
ElMessage.error(msg)
return Promise.reject(new Error(msg))
} else if (code !== 200) {
ElMessage.error(msg)
return Promise.reject('error')
} else {
return res.data
}
}, err => {
return Promise.reject(err)
})
修改后的代码:
//响应拦截
http.interceptors.response.use(res => {
//const code = res.data.status_code || 200;
//const msg = errorCode[code] || res.data.detail || errorCode['default']
//console.log(res);
return res.data
}, err => {
//console.log(err);
const code = err.response.status;//注意:状态码是err.response.status,而不是err.status
const msg = errorCode[code] || errorCode['default'];
if(code == 401){
ElMessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', {
confirmButtonText: '重新登录',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
store.dispatch('loginOut').then(() => {
location.href = '/login';
})
})
} else if (code === 500) {
ElMessage.error("错误500:" + msg)
} else if (code !== 200) {
ElMessage.error("错误:" + msg)
} else {
return err
}
return err
})
总结:错误码不是在响应成功里面拦截,而是在响应失败里面拦截
继续学习,
因为fastapi返回的数据中,中文是转换成unicode代码发送给接口的,于是在前端要解码unicode改成中文,
继续改进成功响应拦截,增加unicode转换成中文代码,改进后的代码如下:
//响应拦截
http.interceptors.response.use(res => {
//const code = res.data.status_code || 200;
//const msg = errorCode[code] || res.data.detail || errorCode['default']
let datas = changearray(res.data);
return datas
}, err => {
const code = err.response.status;//注意:状态码是err.response.status,而不是err.status
const msg = errorCode[code] || errorCode['default'];// || err.data.detail || errorCode['default']
if(err.response.status == 401){
ElMessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', {
confirmButtonText: '重新登录',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
store.dispatch('loginOut').then(() => {
location.href = '/login';
})
})
} else if (err.response.status === 500) {
ElMessage.error("错误500:" + msg)
} else if (err.response.status !== 200) {
ElMessage.error("错误:" + msg)
} else {
console.log('返回err');
return err
}
return err
})
function changearray(arr){
let item = JSON.stringify(arr);
let item1 = toChineseStr(item);
let item2 = JSON.parse(item1);
return item2;
}
function toChineseStr(str) {//unicode转中文
if (str == '') {//空字符串
return '';
}
//例如unicode \\u7cfb 添加了_unicode_,变成 \\u_unicode_7cfb
//然后split('\\\\u')切分字符串生成数组,\\u_unicode_7cfb就变成了一个字符串元素:_unicode_7cfb
//然后循环数组,当循环到这个元素时,
//判断这个元素值含有_unicode_
//然后把这个元素值_unicode_7cfb里面的_unicode_替换成空
//取前面4个字符,得到了7cfb,取第四个字符后面的字符
//把7cfb进行unicode解码,【使用string.fromCharCode( parseInt('7cfb', 16) ) + 第四个字符后面的字符】就转换成了中文
let unicodeStr = str;
let reg=new RegExp('\\\\u','g')//定义要替换的字符串,g表示要全部替换掉
let chineseStr = '';
let itemStr = '';
let itemStr1 = '';
let itemStr2 = '';
unicodeStr = unicodeStr.replace(reg,'\\u_unicode_')//执行替换,吧\\u替换成\\u_unicode_
unicodeStr = unicodeStr.split('\\\\u');//使用split切分数组
for (let i = 0, iLength = unicodeStr.length; i < iLength; i++) {
if(unicodeStr[i].indexOf('_unicode_') !=-1){
itemStr = unicodeStr[i].replace('_unicode_','');
itemStr1 = itemStr.substring(0,4);
itemStr2 = itemStr.substring(4);
chineseStr += String.fromCharCode(parseInt(itemStr1, 16)) + itemStr2;
}else{
chineseStr += unicodeStr[i];
}
}
return chineseStr;
}
接下来做,用户登录后,访问用户信息接口显示管理员信息,
后端fastapi代码如下【后端很简洁】:
main.py增加接口:
#新增接口userInfo【管理员信息接口】
@app.get("/userinfo")
async def userinfo(current_user: User = Depends(get_current_active_user)):
return current_user
login.py相关代码如下:
async def get_current_active_user(current_user: User = Depends(get_current_user)):
"""获取当前用户信息,实际上是作为依赖,注入其他路由以使用。
:param current_user:
:return:
"""
# 如果用户被禁,抛出异常
if current_user.disabled:
raise HTTPException(status_code=400, detail="Inactive user")
return current_user
async def get_current_user(token: str = Depends(oauth2_scheme)):
"""获取当前用户信息,实际上是一个解密token的过程
:param token: 携带的token
:return:
"""
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
# 解密tokens
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
# 从tokens的载荷payload中获取用户名
username: str = payload.get("sub")
# 如果没有获取到,抛出异常
if username is None:
raise credentials_exception
token_data = TokenData(username=username)
except JWTError:
raise credentials_exception
# 从数据库查询用户信息
user = get_user(fake_users_db, username=token_data.username)
if user is None:
raise credentials_exception
return user
# 创建一个加密解密上下文环境(甚至可以不用管这两句话啥意思)
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
def get_user(db, username: str):
"""查询用户
:param db: 模拟的数据库
:param username: 用户名
:return: 返回一个用户的BaseModel(其实就是字典的BaseModel对象,二者可互相转换)
"""
if username in db:
user_dict = db[username]
return UserInDB(**user_dict)
class UserInDB(User):
hashed_password: str
# 这里定义一个字典,来模拟数据库中的数据
fake_users_db = {
"johndoe": {
"uid": "1",
"username": "johndoe",
"full_name": "John Doe",
"avatar": "https://up.enterdesk.com/2021/edpic/c4/9f/09/c49f090757360f843141fe2bab2cfc8f_1.jpg",
"email": "[email protected]",
"hashed_password": "$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW",#默认密码secret
"disabled": False,
"permisson": json_permisson
}
}
VUE修改About.vue如下:
管理员信息
{{userInfo}}
然后就获取到管理员信息了,
最后页面展示如下:
已经成功获得fastapi管理员信息接口,
接下来改进:
一、store.js挂载的member.js添加获得管理员信息的方法
二、about.vue调用store的这个方法,获得管理员信息,并显示
member.js代码如下:
import{getToken,setToken,setTokenType,removeToken} from '@/common/token'
//挂载api接口组件
import api from '@/api/api'
//import { createSocket } from '@/common/websocket'
const user = {
state: {//定义
token: getToken(),
tokentype: '',
uid:'', //管理员id
username: '', //管理员名
fullname: '', //管理员全名
avatar: '', //管理员头像
email:'', //管理员邮箱
permisson:[] //其它备用【例如:权限】
},
mutations: {//赋值
SET_TOKEN: (state, token) => {
state.token = token
},
SET_TOKENTYPE: (state, tokentype) => {
state.tokentype = tokentype
},
SET_UID: (state, uid) => {
state.uid = uid
},
SET_USERNAME: (state, username) => {
state.username = username
},
SET_FULLNAME: (state, fullname) => {
state.fullname = fullname
},
SET_AVATAR: (state, avatar) => {
state.avatar = avatar
},
SET_EMAIL:(state,email)=>{
state.email = email
},
SET_PERMISSON:(state,permisson)=>{
state.permisson = permisson
}
},
actions: {//响应方法
GetInfo({ commit, state }) {//获取管理员信息
return new Promise((resolve, reject) => {
api.getInfo().then(res => {
const user = JSON.parse(JSON.stringify(res)); //绑定管理员信息到常量
const avatar = user.avatar == null ? require("@/assets/img/empty-face.png") : user.avatar;//解析头像地址,没有头像则绑定一张默认头像
commit('SET_USERNAME', user.username) //绑定姓名
commit('SET_AVATAR', avatar) //绑定头像
commit('SET_UID',user.uid); //绑定id
commit('SET_EMAIL',user.email); //绑定email
commit('SET_PERMISSON',user.permisson); //绑定权限
resolve(state)
}).catch(error => {
reject(error)
})
})
},
Login({commit},userInfo){//访问登录接口
console.log('开始登录');
return new Promise((resolve,reject)=>{
api.login(userInfo).then(res=>{
console.log(res);
setToken(res.access_token);
setTokenType(res.token_type);
commit('SET_TOKEN',res.access_token)
commit('SET_TOKENTYPE',res.token_type)
resolve()
}).catch(error=>{
reject(error)
})
})
},
loginOut({ commit, state }) {//退出登录
return new Promise((resolve, reject) => {
console.log(state);
console.log(reject);
commit('SET_TOKEN', '');
commit('SET_TOKENTYPE','')
commit('SET_UID','');
commit('SET_USERNAME','');
commit('SET_FULLNAME','');
commit('SET_AVATAR','');
commit('SET_EMAIL','');
commit('SET_ACCOUNT','');
commit('SET_PERMISSON',[]);
removeToken();
resolve();
})
},
}
}
export default user
about.vue修改如下:
管理员信息
{{userInfo}}
OK,今天就学习到这。