当我们在首页获取一些数据时,但是这些数据请求(例如:"/cates/oneCategory"请求)需要token鉴权时,而我们的用户还没有鉴权时,我们的 @nuxt/axios 请求会得到401的错误
async asyncData({ $axios }) {
// 获取一级分类
let { oneCategory } = await $axios.$get("/cates/oneCategory");
// 对分类数据进行处理
oneCategory = oneCategory.map((val) => ({
...val,
text: val.categoryName,
}));
return {
// 一级分类
oneCategory,
};
},
虽然我们在下面的请求拦截器 $axios.onError
中 使用 redirect('/login')
跳转到了登录页 想让用户去鉴权,但是在地址栏已经到达 /login 后,nuxt默认的错误页面会显示 401错误页面 替换掉 登录页的页面内容
import { Toast } from "vant";
import { httpcode } from "./httpcode";
export default function ({ $axios, store, redirect }) {
// 请求拦截器
$axios.onRequest(() => {
const token = store.state.token;
// 如果存在token
// 使用 @nuxt/axios提供的 setToken 设置 token
token && $axios.setToken(token, "Bearer");
});
// 拦截到错误时
$axios.onError(err => {
// 根据http错误码 提示错误信息
Toast.fail(httpcode[err.response.status])
// 404
if (err.response.status === 404) return redirect("404");
// 未鉴权
if (err.response.status === 401) {
// 跳转登录页
return redirect("/login")
// 因为这里onError对于$axios只是一段异步代码,这里的终止 并不会终止 原来页面的asyncData等钩子函数的继续执行
};
})
}
因为这里onError对于$axios只是一段异步代码,这里的终止 并不会终止 原来页面的asyncData等钩子函数的继续执行 这样的结果是存在问题的,我们要正常显示 登录页才对。 所以,解决问题的关键思路来了:
通过 @nuxt/axios源码可以看出,onError 可以拿到 AxiosError 错误信息,通过调试可以发现,触发 错误页面是在 onError之后。
众所周知,$axios
是Promise
封装的。所以我们可以推断出$axios
应该是在onError之后 Promise.reject(401错误信息)
,才显示nuxt错误页面的。
所以如果我们在这个之前 返回 Promise.resolve(), 401的错误就会被避开了,因为 await $axios.$get(...)
还没有等到Promise.reject(),就提前得到了我们给的 Promise.resolve()
但是 返回 Promise.resolve(),$axios.$get(....)
会得到 undefined
,
所以:
import { Toast } from "vant";
import { httpcode } from "./httpcode";
export default function ({ $axios, store, redirect }) {
// 请求拦截器
$axios.onRequest(() => {
const token = store.state.token;
// 如果存在token
// 使用 @nuxt/axios提供的 setToken 设置 token
token && $axios.setToken(token, "Bearer");
});
// 拦截到错误时
$axios.onError(err => {
// 提示错误信息
Toast.fail(httpcode[err.response.status])
// 404
if (err.response.status === 404) return redirect("404");
// 未鉴权
if (err.response.status === 401) {
// 跳转登录页
redirect("/login")
// 因为这里onError对于$axios只是一段异步执行,这里终止并不会终止 原本asyncData等钩子函数的执行
// 为了解决跳转路由后还会显示错误页面的问题,
// 在返回Promise.reject()之前返回Prmoise.resolve(),防止跳转到错误页
// 因为$get()等方法是直接获取get()结果内部的data,所以,我们给出Promise.reject({data:{}}),
// 最起码保障让$get()拿到空对象{},以防止外界解构赋值时,再次跳转到错误页
return Promise.resolve({ data: {} })
};
})
}
所以,解决的关键就是 这句 return Promise.resolve({ data: {} })
就像注释中说的,这里 resolve()的结果是先给了 $axios.get()
, 是$axios.$get()
封装前的方法,$get()
拿到的是 get()
中的data
对象,我们使用 $get()
,所以 resolve({data:{}})
,保障 $get()
拿到一个空对象 {}
,也保障外界可以解构赋值
不然,我们在使用asyncData等提前渲染的钩子中,后面还可能发生使用数据会遇到的错误
最后,我们要在每次获取数据时,判断结构出的数据是否是 undefined,然后使用。
async asyncData({ $axios }) {
// 获取一级分类
let { oneCategory } = await $axios.$get("/cates/oneCategory");
// 判断数据是否获取成功
if (oneCategory)
oneCategory = oneCategory.map((val) => ({
id: val.id,
text: val.categoryName,
}));
// 这里返回出去的oneCategory虽然是undefined,但是页面已经跳转,原页面模板不会解析,也就不会使用到 oneCategory,后面的也就不会有其它问题
return {
// 一级分类
oneCategory,
};
},
mounted() {
console.log("可以发现,mounted不会执行");
},
再尝试请求未鉴权的接口,登录页跳转后正常显示