feat: 优化 request 异常处理

This commit is contained in:
winixt 2021-05-22 17:26:36 +08:00
parent f636b37df9
commit 3f40ebe08b
6 changed files with 112 additions and 52 deletions

View File

@ -65,7 +65,6 @@ export const request = {
// 特殊 code 处理逻辑 // 特殊 code 处理逻辑
}, },
404(error) { 404(error) {
}, },
default(error) { default(error) {
// 异常统一处理 // 异常统一处理
@ -75,6 +74,32 @@ export const request = {
...otherConfigs ...otherConfigs
} }
``` ```
#### skipErrorHandler
- 类型: `boolean | string | number | array<string | number>`
- 默认值: ``
- 详情:
指定当前请求的某些错误状态不走 `errorHandler`,单独进行处理。如果设置为 `true`,当前请求的错误处理都不走 `errorHandler`
- 示列:
```js
import {request} from '@fesjs/fes';
request('/api/login', null, {
skipErrorHandler: '110'
}).then((res) => {
// do something
}).catch((err) => {
// 这里处理 code 为 110 的异常
// 此时 errorHandler[110] 函数不会生效,也不会执行 errorHandler.default
})
```
## 使用 ## 使用
### 发起一个普通 post 请求 ### 发起一个普通 post 请求

View File

@ -39,6 +39,7 @@ import {
const CACHE_KEY_PREFIX = '__FES_REQUEST_CACHE:'; const CACHE_KEY_PREFIX = '__FES_REQUEST_CACHE:';
const CACHE_TYPE = { const CACHE_TYPE = {
merge: 'merge', // merge 重复请求
ram: 'ram', ram: 'ram',
session: 'sessionStorage', session: 'sessionStorage',
local: 'localStorage' local: 'localStorage'
@ -175,6 +176,11 @@ function handleCachingQueueError(ctx, config) {
} }
} }
/**
* TODO 添加一种 merge 类型
* merge 当期所有请求按顺序发起请求只要有一个成功所有请求都成功并且跳过后续的请求否则所有请求都失败
* merge 错误提示
*/
export default async (ctx, next) => { export default async (ctx, next) => {
const { config } = ctx; const { config } = ctx;
@ -200,11 +206,14 @@ export default async (ctx, next) => {
const requestdata = checkHttpRequestHasBody(config.method) ? config.data : config.params; const requestdata = checkHttpRequestHasBody(config.method) ? config.data : config.params;
if (!ctx.error && ctx.response && canCache(requestdata) && canCache(ctx.response.data)) { if (!ctx.error && ctx.response && canCache(requestdata) && canCache(ctx.response.data)) {
handleCachingQueueSuccess(ctx, config, ctx.response.data); handleCachingQueueSuccess(ctx, config, ctx.response.data);
setCacheData({
key: ctx.key, if (config.cache.cacheType !== CACHE_TYPE.merge) {
data: ctx.response.data, setCacheData({
...config.cache key: ctx.key,
}); data: ctx.response.data,
...config.cache
});
}
} else { } else {
handleCachingQueueError(ctx, config); handleCachingQueueError(ctx, config);
} }

View File

@ -65,6 +65,7 @@ function getRequestInstance() {
addRequestInterceptors(instance, requestInterceptors); addRequestInterceptors(instance, requestInterceptors);
addResponseInterceptors(instance, responseInterceptors); addResponseInterceptors(instance, responseInterceptors);
// 洋葱模型内部应该这是对数据的处理,避免有副作用调用
scheduler.use(paramsProcess) scheduler.use(paramsProcess)
.use(genRequestKey) .use(genRequestKey)
.use(cacheControl) .use(cacheControl)
@ -120,6 +121,52 @@ function createContext(userConfig) {
}; };
} }
function getResponseCode(response) {
if (response) {
if (response._rawData) return response._rawData.code;
if (response.data) return response.data.code;
}
return null;
}
function skipErrorHandlerToObj(skipErrorHandler = []) {
if (!Array.isArray(skipErrorHandler)) {
skipErrorHandler = [skipErrorHandler];
}
return skipErrorHandler.reduce((acc, cur) => {
acc[cur] = true;
return acc;
}, {});
}
function handleRequestError({
errorHandler = {},
error,
response,
config
}) {
// 跳过所有错误类型处理
if (config.skipErrorHandler === true) return;
const skipObj = skipErrorHandlerToObj(config.skipErrorHandler);
const resCode = getResponseCode(response);
let errorKey = 'default';
if (resCode && errorHandler[resCode]) {
errorKey = resCode;
} else if (error.type && errorHandler[error.type]) {
errorKey = error.type;
} else if (error.response && errorHandler[error.response.status]) {
errorKey = error.response.status;
}
if (!skipObj[errorKey] && errorHandler[errorKey]) {
errorHandler[errorKey](error);
}
}
export const request = (url, data, options = {}) => { export const request = (url, data, options = {}) => {
if (typeof options === 'string') { if (typeof options === 'string') {
options = { options = {
@ -136,6 +183,7 @@ export const request = (url, data, options = {}) => {
if (!context.error) { if (!context.error) {
return context.config.useResonse ? context.response : context.response.data; return context.config.useResonse ? context.response : context.response.data;
} }
handleRequestError(context);
return Promise.reject(context.error); return Promise.reject(context.error);
}); });
}; };

View File

@ -1,41 +1,16 @@
import { isObject } from './helpers'; import { isObject } from './helpers';
function handleAbnormalCode(errorHandler = {}, code, response) { // 错误处理等副作用网上提
if (errorHandler[code]) {
errorHandler[code](response.data);
} else if (errorHandler.default) {
// 处理其他异常
errorHandler.default({
response
});
}
}
function handleRequestError(errorHandler = {}, error) {
if (error.type && errorHandler[error.type]) {
errorHandler[error.type](error);
} else if (error.response && errorHandler[error.response.status]) {
errorHandler[error.response.status](error);
} else if (errorHandler.default) {
errorHandler.default(error);
}
}
export default async (ctx, next) => { export default async (ctx, next) => {
const { const {
error,
errorHandler = {},
response, response,
config config
} = ctx; } = ctx;
if (!config.closeResDataCheck && response && isObject(response.data)) { if (!config.closeResDataCheck && response && isObject(response.data)) {
const code = response.data.code; const code = response.data.code;
if (code !== '0') { if (code !== '0') {
handleAbnormalCode(errorHandler, code, response);
ctx.error = response; // code 不为零进入 reject ctx.error = response; // code 不为零进入 reject
} }
} else if (error) {
handleRequestError(errorHandler, error);
} }
await next(); await next();

View File

@ -1,7 +1,7 @@
export const request = { export const request = {
errorHandler: { errorHandler: {
111(responseData) { 111() {
console.log(responseData); console.log('root:111');
}, },
500() { 500() {
console.log('500 error'); console.log('500 error');

View File

@ -12,18 +12,13 @@
} }
</config> </config>
<script> <script>
import { ref, onMounted } from 'vue'; import { ref } from 'vue';
import { useRouter, request } from '@fesjs/fes'; import { request } from '@fesjs/fes';
export default { export default {
setup() { setup() {
const fes = ref('fes upgrade to vue3'); const fes = ref('fes upgrade to vue3');
const rotate = ref(90); const rotate = ref(90);
const router = useRouter();
onMounted(() => {
console.log(router);
console.log('mounted1!!');
});
const clickIcon = () => { const clickIcon = () => {
console.log('click Icon'); console.log('click Icon');
}; };
@ -58,20 +53,28 @@ export default {
// }); // });
// }, 3200); // }, 3200);
// request('/api', null, {
// cache: true
// }).then((res) => {
// console.log(res);
// });
// request('/api', null, {
// cache: true
// }).then((res) => {
// console.log(res);
// });
// request('/api', null, {
// cache: true
// }).then((res) => {
// console.log(res);
// });
request('/api', null, { request('/api', null, {
cache: true // skipErrorHandler: [500]
}).then((res) => {
console.log(res);
});
request('/api', null, {
cache: true
}).then((res) => {
console.log(res);
});
request('/api', null, {
cache: true
}).then((res) => { }).then((res) => {
console.log(res); console.log(res);
}).catch((err) => {
console.log('inner error', err);
}); });
return { return {
fes, fes,