本项目是基于ant-design开发的一个前端项目,项目的上线地址:http://user-front.66bond.com/
本项目的开发软件:
- webstorm2021:关注公众号”青椒工具”,发送”webstorm”获取安装包下载链接;
为了让后端统一处理各种异常情况,我们把后端发往前端的数据做了简单的封装如下:
1 2 3 4 5 6
| { "code":, "data": "message": "description": }
|
所以前端要做修改:
与后端对应,前端的typings.d.ts文件中,创建一个对应的数据类型:
1 2 3 4 5 6
| type BaseResponse<T>={ code:number, data:T, message: string, descritpion: string }
|
然后以register访问的/api/user/register为例;
1 2 3 4 5 6 7 8 9 10
| export async function register(body: API.RegisterParams, options?: { [key: string]: any }) { return request<API.BaseResponse<API.RegisterResult>>('/api/user/register', { method: 'POST', headers: { 'Content-Type': 'application/json', }, data: body, ...(options || {}), }); }
|
Register/index.tsx的修改如下,主要将看else语句中,将descritption设置给Error,然后在异常处理中,将错误描述信息再显示到前端。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| try { const res = await register({ ...values, type, }); if(res.code === 0 && res.data > 0){ const defaultLoginSuccessMessage = '注册成功!'; message.success(defaultLoginSuccessMessage); await fetchUserInfo(); if (!history) return; const { query } = history.location; const { redirect } = query as { redirect: string; }; history.push('/user/login'); return; }else throw new Error(res.description); } catch (error: any) { const defaultLoginFailureMessage = "注册失败,请重试!!"; message.error(error.message??defaultLoginFailureMessage); } };
|
我们采用数据库中已有的账户注册,这个时候可以发现前端会提示对应的错误,如图1所示。

图1 前端提示注册账户重复
与Register类似,前端其他的api接口都需要进行适配,包括:
- login, /api/user/login
- searchUsers, /api/user/search
- currentUser, /api/user/current
- outLogin, /api/user/logout
- register, /api/user/register
有这么多方法,难道我们对每个方法都要写一遍类似如下的判断逻辑么:
1 2 3
| if(res.code === 0 && res.data > 0){ .... }
|
有没有统一处理、集中处理的方法?
思路:对来自后台的response做统一的处理,从response中取出data;或者根据response集中做错误处理,比如用户未登录、没有权限之类。优势:不用在每个接口请求中都去写相同的逻辑。
要实现上述思路,我们需要写一个response的全局拦截器,在拦截器中做统一的处理。
我们根据/Register/index.tsx中的request方法,可以顺藤摸瓜,跟踪到src/.umi/plugin-request/requests.ts中的请求拦截器和响应拦截器:
1 2 3 4 5 6 7 8 9
| const requestInterceptors = requestConfig.requestInterceptors || []; const responseInterceptors = requestConfig.responseInterceptors || []; requestInterceptors.map((ri) => { requestMethodInstance.interceptors.request.use(ri); }); responseInterceptors.map((ri) => { requestMethodInstance.interceptors.response.use(ri); });
|
此处我们可以定义自己的拦截器;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| requestConfig.responseInterceptors = [ function(response:Response, options:RequestOptionsInit): Response | Promise<Response>{ console.log("全局response拦截器",response); } ];
const requestInterceptors = requestConfig.requestInterceptors || []; const responseInterceptors = requestConfig.responseInterceptors || []; requestInterceptors.map((ri) => { requestMethodInstance.interceptors.request.use(ri); }); responseInterceptors.map((ri) => { requestMethodInstance.interceptors.response.use(ri); });
|
再登录注册页面,点击”注册”按钮,打开浏览器开发者工具/终端,可以看到上述的”全局Response拦截器”字眼,并且没有按照预期跳转到登录页;
解释:说明我们上面写的requestConfig.responseInterceptors,开始工作;出现错误是因为上述拦截器没有返回数据;
新的问题:如何返回数据,尤其是data部分。现在的response中(从终端输出)没有看到对应的数据。
不能直接从数据或者源码中获取答案,那就试试百度和官方文档,百度以关键词”umi request全局响应”,可以搜到如下的网页:
1
| https://blog.csdn.net/huantai3334/article/details/116780020
|
其中获取数据的代码行:
1
| const data = await response.clone().json();
|
将上述这行代码添加到我们的代码中,新的问题:我们之前写的代码消失了。原来在src/.umi/目录下的代码是框架动态自动生成的,新生成的会覆盖掉我们之前修改的,所以我们还得写到其他地方。
参照上述csdn网页中的代码,我们在src下创建/plugins/globalRequests.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| import {extend} from 'umi-request'; import {message} from "antd"; import {history} from "@@/core/history"; import {stringify} from "querystring";
const request = extend({ credentials: 'include', });
request.interceptors.request.use((url, options): any => { console.log('do request url = {}', url);
return { url, options: { ...options, headers: { }, }, }; });
request.interceptors.response.use(async (response, options): Promise<any> => { const res = await response.clone().json();
if(res.code === 0){ return res.data; }
if(res.code === 40100){ message.error('请先登录'); history.replace({ pathname: '/user/login', search: stringify({ redirect: location.pathname, }), }); }else{ message.error(res.description) } return res.data; });
export default request;
|
完成globalRequests.ts后,很重要的一个点,要在api.ts中,将如下语句:
1
| import { request } from 'umi'
|
修改为:
1
| import request from '@/plugins/globalRequest';
|
完成上述修改后,前端可以正常地登录,展示数据,以及logout。但是唯一的问题:在注册成功,跳转到登录页时,还会弹出”请先登录”的错误;按道理不应该弹出,如图2所示。

图2 注册后提示请先登录错误
我们修改下response拦截器的代码,增加一个判断:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
request.interceptors.response.use(async (response, options): Promise<any> => { const res = await response.clone().json(); if(res.code === 0){ return res.data; } if(res.code === 40100){ if(history.location.pathname !== '/user/register') { message.error('请先登录'); history.replace({ pathname: '/user/login', search: stringify({ redirect: location.pathname, }), }); }else{ history.replace({ pathname: '/user/login', search: stringify({ redirect: location.pathname, }), }); } }else{ message.error(res.description) } return res.data; });
|
然后,将Register/index.tsx中的handleSubmit的逻辑修改为如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| const handleSubmit = async (values: API.RegisterParams) => {
const {userPassword, checkPassword} = values; if(userPassword !== checkPassword){ message.error('两次密码输入不相同!!'); return; }
try { const id = await register({ ...values, type, }); if(id >= 0) { const defaultLoginSuccessMessage = '注册成功!'; message.success(defaultLoginSuccessMessage); await fetchUserInfo(); if (!history) return; const {query} = history.location; const {redirect} = query as { redirect: string; }; history.push('/user/login'); return; } } catch (error: any) { const defaultLoginFailureMessage = "注册失败,请重试!!"; message.error(defaultLoginFailureMessage); } };
|
经过上述的修改,注册后到登录页的错误提示消失。
至此,前端对后端封装的异常完成了适配。
参考资料:
本文参考自如下知识星球中的视频教程,更多的完整的相关视频教程,见如下的收费知识星球,近3万人的学习社区,
编程有人同行,学习不再迷茫:
