fix: request plugin

This commit is contained in:
bac-joker 2020-12-30 14:25:14 +08:00
parent 2ee1b659a0
commit 297b48503d
38 changed files with 162 additions and 107 deletions

View File

@ -6,7 +6,7 @@ import { join } from "path";
const headPkgs = [ const headPkgs = [
"fes-runtime", "fes-runtime",
"fes-core", "fes-compiler",
"fes", "fes",
"fes-preset-built-in", "fes-preset-built-in",
"fes-plugin-request", "fes-plugin-request",

View File

@ -1,7 +1,7 @@
{ {
"name": "@webank/fes-core", "name": "@webank/fes-compiler",
"version": "2.0.0", "version": "2.0.0",
"description": "@webank/fes-core", "description": "@webank/fes-compiler",
"main": "lib/index.js", "main": "lib/index.js",
"types": "lib/index.d.ts", "types": "lib/index.d.ts",
"files": [ "files": [

View File

@ -61,7 +61,7 @@ export default class Logger {
delete this.profilers[id]; delete this.profilers[id];
process.stderr.write(`${this.PROFILE} `); process.stderr.write(`${this.PROFILE} `);
msg = `${this.PROFILE} ${chalk.cyan( msg = `${this.PROFILE} ${chalk.cyan(
`${namespace}`, `${namespace}`
)} Completed in ${this.formatTiming(time - timeEnd)}`; )} Completed in ${this.formatTiming(time - timeEnd)}`;
console.log(msg); console.log(msg);
} else { } else {

View File

@ -14,7 +14,9 @@
"author": "", "author": "",
"license": "MIT", "license": "MIT",
"peerDependencies": { "peerDependencies": {
"axios": "0.21.1",
"@webank/fes": "^2.0.0" "@webank/fes": "^2.0.0"
},
"dependencies": {
"axios": "0.21.1"
} }
} }

View File

@ -5,6 +5,7 @@ export default (api) => {
api.addRuntimePluginKey(() => 'request'); api.addRuntimePluginKey(() => 'request');
// 配置 // 配置
api.describe({ api.describe({
key: 'request',
config: { config: {
schema(joi) { schema(joi) {
return joi.object({ return joi.object({
@ -15,7 +16,7 @@ export default (api) => {
}); });
}, },
default: { default: {
dataField: 'result' dataField: ''
} }
} }
}); });
@ -29,7 +30,7 @@ export default (api) => {
api.writeTmpFile({ api.writeTmpFile({
path: absoluteFilePath, path: absoluteFilePath,
content: requestTemplate content: requestTemplate
.replace('REPLACE_DATA_FIELD', dataField) .replace('REPLACE_DATA_FIELD', JSON.stringify(dataField))
}); });
}); });

View File

@ -44,7 +44,7 @@ const CACHE_TYPE = {
local: 'localStorage' local: 'localStorage'
}; };
const CACHE_DATA = new Map(); const CACHE_DATA_MAP = new Map();
function genInnerKey(key, cacheType) { function genInnerKey(key, cacheType) {
if (cacheType !== CACHE_TYPE.ram) { if (cacheType !== CACHE_TYPE.ram) {
@ -53,17 +53,18 @@ function genInnerKey(key, cacheType) {
return key; return key;
} }
function canCache(requestData) { function canCache(data) {
return isObject(requestData) || isString(requestData) || isURLSearchParams(requestData); return !data || isObject(data) || isString(data) || Array.isArray(data) || isURLSearchParams(data);
} }
function setCacheData({ function setCacheData({
key, key,
cacheType, cacheType = 'ram',
data, data,
cacheTime = 1000 * 60 * 3 cacheTime = 1000 * 60 * 3
}) { }) {
const _key = genInnerKey(key, cacheType); const _key = genInnerKey(key, cacheType);
const currentCacheData = { const currentCacheData = {
cacheType, cacheType,
data, data,
@ -83,7 +84,7 @@ function setCacheData({
} }
} }
} else { } else {
CACHE_DATA.set(_key, currentCacheData); CACHE_DATA_MAP.set(_key, currentCacheData);
} }
} }
@ -111,33 +112,36 @@ function getCacheData({ key, cacheType = 'ram' }) {
return null; return null;
} }
} else { } else {
const currentCacheData = CACHE_DATA.get(_key); const currentCacheData = CACHE_DATA_MAP.get(_key);
if (currentCacheData && !isExpire(currentCacheData)) { if (currentCacheData && !isExpire(currentCacheData)) {
return currentCacheData.data; return currentCacheData.data;
} }
CACHE_DATA.delete(_key); CACHE_DATA_MAP.delete(_key);
return null; return null;
} }
} }
export default (ctx, next) => { export default async (ctx, next) => {
const { config } = ctx; const { config } = ctx;
if (config.cache) { if (config.cache) {
const data = checkHttpRequestHasBody(config.method) ? config.data : config.params; const cacheData = getCacheData({ key: ctx.key, cacheType: config.cache.cacheType });
if (canCache(data)) { if (cacheData) {
ctx.response = { ctx.response = {
data: getCacheData({ key: ctx.key, cacheType: config.cache.cacheType }) data: cacheData
}; };
return; return;
} }
} }
next(); await next();
if (config.cache) { if (config.cache) {
const requestdata = checkHttpRequestHasBody(config.method) ? config.data : config.params;
if (ctx.response && canCache(requestdata) && canCache(ctx.response.data)) {
setCacheData({ setCacheData({
key: ctx.key, key: ctx.key,
data: ctx.response.data, data: ctx.response.data,
...config.cache ...config.cache
}); });
} }
}
}; };

View File

@ -5,11 +5,11 @@ import { isURLSearchParams } from './helpers';
* 一个请求同时包含 data | params 参数的设计本身不合理 * 一个请求同时包含 data | params 参数的设计本身不合理
* 不对这种情况进行兼容 * 不对这种情况进行兼容
*/ */
export default function genRequestKey(ctx, next) { export default async function genRequestKey(ctx, next) {
const { url, data, method } = ctx.config; const { url, data, method } = ctx.config;
if (isURLSearchParams(data)) { if (isURLSearchParams(data)) {
ctx.key = `${url}${data.toString()}${method}`; ctx.key = `${url}${data.toString()}${method}`;
} }
ctx.key = `${url}${JSON.stringify(data)}${method}`; ctx.key = `${url}${JSON.stringify(data)}${method}`;
next(); await next();
} }

View File

@ -1,11 +1,11 @@
import { checkHttpRequestHasBody, trimObj } from 'helpers'; import { checkHttpRequestHasBody, trimObj } from './helpers';
export default (ctx, next) => { export default async (ctx, next) => {
const config = ctx.config; const config = ctx.config;
if (checkHttpRequestHasBody(config.method)) { if (checkHttpRequestHasBody(config.method)) {
config.data = trimObj(config.data); config.data = trimObj(config.data);
} else { } else {
config.params = trimObj(config.params); config.params = trimObj(config.params);
} }
next(); await next();
}; };

View File

@ -1,17 +1,17 @@
const requestQueue = new Map(); const requestMap = new Map();
export default (ctx, next) => { export default async (ctx, next) => {
const key = ctx.key; const key = ctx.key;
if (requestQueue.get(key)) { if (requestMap.get(key)) {
ctx.error = { ctx.error = {
type: 'REPEAT', type: 'REPEAT',
msg: '重复请求' msg: '重复请求'
}; };
return; return;
} }
requestQueue.set(key, true); requestMap.set(key, true);
next(); await next();
requestQueue.delete(key); requestMap.delete(key);
}; };

View File

@ -1,6 +1,6 @@
import axios from 'axios'; import axios from 'axios';
import { ApplyPluginsType, plugin } from '@webank/fes'; import { ApplyPluginsType, plugin } from '@webank/fes';
import scheduler from 'scheduler'; import scheduler from './scheduler';
import { import {
checkHttpRequestHasBody, checkHttpRequestHasBody,
isFunction isFunction
@ -10,6 +10,7 @@ import setDataField from './setDataField';
import paramsProcess from './paramsProcess'; import paramsProcess from './paramsProcess';
import genRequestKey from './genRequestKey'; import genRequestKey from './genRequestKey';
import preventRepeatReq from './preventRepeatReq'; import preventRepeatReq from './preventRepeatReq';
import throttle from './throttle';
import cacheControl from './cacheControl'; import cacheControl from './cacheControl';
import resDataAdaptor from './resDataAdaptor'; import resDataAdaptor from './resDataAdaptor';
import resErrorProcess from './resErrorProcess'; import resErrorProcess from './resErrorProcess';
@ -32,22 +33,21 @@ function addResponseInterceptors(instance, interceptors) {
addInterceptors(instance, interceptors, 'response'); addInterceptors(instance, interceptors, 'response');
} }
function axiosMiddleware(context, next) { async function axiosMiddleware(context, next) {
context.instance.request(context.config).then((response) => { try {
context.response = response; context.response = await context.instance.request(context.config);
}).catch((error) => { } catch (error) {
context.error = error; context.error = error;
}).finally(() => { }
next(); await next();
});
} }
function getRequestInstance() { function getRequestInstance() {
const { const {
responseDataAdaptor, responseDataAdaptor,
errorConfig, errorConfig,
requestInterceptors, requestInterceptors = [],
responseInterceptors, responseInterceptors = [],
errorHandler, errorHandler,
...otherConfigs ...otherConfigs
} = plugin.applyPlugins({ } = plugin.applyPlugins({
@ -65,14 +65,15 @@ function getRequestInstance() {
addRequestInterceptors(instance, requestInterceptors); addRequestInterceptors(instance, requestInterceptors);
addResponseInterceptors(instance, responseInterceptors); addResponseInterceptors(instance, responseInterceptors);
scheduler.use(paramsProcess); scheduler.use(paramsProcess)
scheduler.use(genRequestKey); .use(genRequestKey)
scheduler.use(preventRepeatReq); .use(preventRepeatReq)
scheduler.use(cacheControl); .use(throttle)
scheduler.use(axiosMiddleware); .use(cacheControl)
scheduler.use(resDataAdaptor); .use(axiosMiddleware)
scheduler.use(resErrorProcess); .use(resDataAdaptor)
scheduler.use(setDataField); .use(resErrorProcess)
.use(setDataField);
return { return {
context: { context: {
@ -96,6 +97,7 @@ function userConfigHandler(url, data, options = {}) {
} else { } else {
options.params = data; options.params = data;
} }
return options;
} }
let currentRequestInstance = null; let currentRequestInstance = null;
@ -117,10 +119,10 @@ export const request = (url, data, options = {}) => {
const userConfig = userConfigHandler(url, data, options); const userConfig = userConfigHandler(url, data, options);
const context = createContext(userConfig); const context = createContext(userConfig);
return currentRequestInstance.request(context).then((ctx) => { return currentRequestInstance.request(context).then(() => {
if (!ctx.error) { if (!context.error) {
return ctx.config.useResonse ? ctx.response : ctx.response.data; return context.config.useResonse ? context.response : context.response.data;
} }
return Promise.reject(ctx.error); return Promise.reject(context.error);
}); });
}; };

View File

@ -1,8 +1,8 @@
import { isFunction, isObject, isString } from './helpers'; import { isFunction, isObject, isString } from './helpers';
export default ({ response, responseDataAdaptor }, next) => { export default async ({ response, responseDataAdaptor }, next) => {
if (isFunction(responseDataAdaptor) && (isObject(response.data) || isString(response.data))) { if (isFunction(responseDataAdaptor) && (isObject(response.data) || isString(response.data))) {
response.data = responseDataAdaptor(response.data); response.data = responseDataAdaptor(response.data);
} }
next(); await next();
}; };

View File

@ -1,29 +1,19 @@
import { isObject, isFunction } from 'helpers'; import { isObject } from './helpers';
function resErrorProcess(error, customerErrorHandler) { export default async ({
if (isFunction(error)) {
error();
} else {
customerErrorHandler && customerErrorHandler(error);
}
}
export default ({
error, error,
errorConfig, errorHandler = {},
errorHandler,
response response
}, next) => { }, next) => {
const _errorConfig = Object.assign({ if (response && isObject(response.data) && response.data.code !== '0') {
403: '用户得到授权,但访问是禁止的' errorHandler[response.data.code] && errorHandler[response.data.code](response);
}, errorConfig); } else if (error) {
if (error.type) {
if (isObject(response.data) && response.data.code !== '0') { errorHandler[error.type] && errorHandler[error.type](error);
resErrorProcess(_errorConfig[response.data.code] || response.data.msg || response.data.errorMessage || response.data.errorMsg || '服务异常', errorHandler); } else if (error.response) {
} else if (error && error.response && _errorConfig[error.response.status]) { errorHandler[error.response.status] && errorHandler[error.response.status](error);
resErrorProcess(_errorConfig[error.response.status], errorHandler); }
} }
next(); await next();
}; };

View File

@ -30,4 +30,4 @@ class Scheduler {
} }
} }
export default Scheduler(); export default new Scheduler();

View File

@ -1,10 +1,10 @@
import { isObject } from './helpers'; import { isObject } from './helpers';
export default (ctx, next) => { export default async (ctx, next) => {
const { dataField, response } = ctx; const { dataField, response } = ctx;
if (isObject(response.data) && dataField) { if (response && isObject(response.data) && dataField) {
ctx.response._rawData = response.data; ctx.response._rawData = response.data;
ctx.response.data = response.data[dataField]; ctx.response.data = response.data[dataField];
} }
next(); await next();
}; };

View File

@ -0,0 +1,16 @@
const throttleMap = new Map();
export default async (ctx, next) => {
if (ctx.config.throttle) {
if (throttleMap.get(ctx.key) >= Date.now()) {
ctx.error = {
type: 'FREQUENTLY',
msg: '请求过于频繁'
};
return;
}
}
await next();
throttleMap.set(ctx.key, Date.now() + ctx.config.throttle);
};

View File

@ -30,7 +30,7 @@
"@vue/babel-plugin-jsx": "^1.0.0-rc.5", "@vue/babel-plugin-jsx": "^1.0.0-rc.5",
"@vue/compiler-sfc": "^3.0.4", "@vue/compiler-sfc": "^3.0.4",
"@vue/preload-webpack-plugin": "1.1.2", "@vue/preload-webpack-plugin": "1.1.2",
"@webank/fes-core": "^2.0.0", "@webank/fes-compiler": "^2.0.0",
"cliui": "6.0.0", "cliui": "6.0.0",
"html-webpack-plugin": "^3.2.0", "html-webpack-plugin": "^3.2.0",
"html-webpack-tags-plugin": "2.0.17", "html-webpack-tags-plugin": "2.0.17",

View File

@ -1,6 +1,6 @@
import { relative } from 'path'; import { relative } from 'path';
import { existsSync } from 'fs'; import { existsSync } from 'fs';
import { Logger } from '@webank/fes-core'; import { Logger } from '@webank/fes-compiler';
import { import {
cleanTmpPathExceptCache, cleanTmpPathExceptCache,
getBundleAndConfigs, getBundleAndConfigs,

View File

@ -1,7 +1,7 @@
import { join } from 'path'; import { join } from 'path';
import { chokidar, winPath, lodash } from '@umijs/utils'; import { chokidar, winPath, lodash } from '@umijs/utils';
import { existsSync, readFileSync } from 'fs'; import { existsSync, readFileSync } from 'fs';
import { isPluginOrPreset, PluginType } from '@webank/fes-core'; import { isPluginOrPreset, PluginType } from '@webank/fes-compiler';
function getPlugins(opts) { function getPlugins(opts) {
return Object.keys({ return Object.keys({

View File

@ -7,6 +7,9 @@ export default {
// __VUE_OPTIONS_API__: true, // __VUE_OPTIONS_API__: true,
// __VUE_PROD_DEVTOOLS__: false // __VUE_PROD_DEVTOOLS__: false
}, },
request: {
dataField: 'fileTemplateList'
},
html: { html: {
options: { options: {
title: '海贼王' title: '海贼王'

View File

@ -35,6 +35,7 @@
"dependencies": { "dependencies": {
"vue": "^3.0.4", "vue": "^3.0.4",
"@webank/fes": "^2.0.0", "@webank/fes": "^2.0.0",
"@webank/fes-plugin-icon": "^1.0.0" "@webank/fes-plugin-icon": "^1.0.0",
"@webank/fes-plugin-request": "^1.0.0"
} }
} }

View File

@ -0,0 +1,7 @@
export const request = {
errorConfig: {
404() {
console.log('to 404 page');
}
}
};

View File

@ -12,8 +12,7 @@
</config> </config>
<script> <script>
import { ref, onMounted } from 'vue'; import { ref, onMounted } from 'vue';
import { useRouter } from '@webank/fes'; import { useRouter, request } from '@webank/fes';
// import Icon from '@/components/Icon';
export default { export default {
setup() { setup() {
@ -23,14 +22,44 @@ export default {
onMounted(() => { onMounted(() => {
console.log(router); console.log(router);
console.log('mounted1!!'); console.log('mounted1!!');
// router.push('/onepiece');
});
onMounted(() => {
console.log('mounted2!!');
}); });
const clickIcon = () => { const clickIcon = () => {
console.log('click Icon'); console.log('click Icon');
}; };
request('api', {}, {
cache: {
cacheType: 'ram',
cacheTime: 5 * 1000
}
}).then((data) => {
console.log(data);
}).catch((err) => {
console.log(err);
});
setTimeout(() => {
request('api', {}, {
cache: {
cacheType: 'ram',
cacheTime: 5 * 1000
}
}).then((data) => {
console.log(data);
}).catch((err) => {
console.log(err);
});
}, 200);
setTimeout(() => {
request('api', {}, {
cache: {
cacheType: 'ram',
cacheTime: 5 * 1000
}
}).then((data) => {
console.log(data);
}).catch((err) => {
console.log(err);
});
}, 6000);
return { return {
fes, fes,
rotate, rotate,

View File

@ -31,7 +31,7 @@
], ],
"dependencies": { "dependencies": {
"@webank/fes-preset-built-in": "^2.0.0", "@webank/fes-preset-built-in": "^2.0.0",
"@webank/fes-core": "^2.0.0", "@webank/fes-compiler": "^2.0.0",
"@webank/fes-runtime": "^2.0.0", "@webank/fes-runtime": "^2.0.0",
"@umijs/utils": "3.3.3", "@umijs/utils": "3.3.3",
"resolve-cwd": "^3.0.0" "resolve-cwd": "^3.0.0"

View File

@ -1,5 +1,5 @@
import { dirname } from 'path'; import { dirname } from 'path';
import { Service as CoreService } from '@webank/fes-core'; import { Service as CoreService } from '@webank/fes-compiler';
class Service extends CoreService { class Service extends CoreService {
constructor(opts) { constructor(opts) {

View File

@ -4215,10 +4215,10 @@ aws4@^1.8.0:
resolved "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" resolved "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59"
integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==
axios@^0.20.0: axios@0.21.1:
version "0.20.0" version "0.21.1"
resolved "https://registry.npmjs.org/axios/-/axios-0.20.0.tgz#057ba30f04884694993a8cd07fa394cff11c50bd" resolved "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8"
integrity sha512-ANA4rr2BDcmmAQLOKft2fufrtuvlqR+cXNNinUmvfeSNCOF98PZL+7M/v1zIdGo7OLjEA9J2gXJL+j4zGsl0bA== integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==
dependencies: dependencies:
follow-redirects "^1.10.0" follow-redirects "^1.10.0"
@ -7300,11 +7300,16 @@ flush-write-stream@^1.0.0, flush-write-stream@^1.0.2:
inherits "^2.0.3" inherits "^2.0.3"
readable-stream "^2.3.6" readable-stream "^2.3.6"
follow-redirects@^1.0.0, follow-redirects@^1.10.0: follow-redirects@^1.0.0:
version "1.13.0" version "1.13.0"
resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz#b42e8d93a2a7eea5ed88633676d6597bc8e384db" resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz#b42e8d93a2a7eea5ed88633676d6597bc8e384db"
integrity sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA== integrity sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==
follow-redirects@^1.10.0:
version "1.13.1"
resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.1.tgz#5f69b813376cee4fd0474a3aba835df04ab763b7"
integrity sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg==
for-in@^0.1.3: for-in@^0.1.3:
version "0.1.8" version "0.1.8"
resolved "https://registry.npmjs.org/for-in/-/for-in-0.1.8.tgz#d8773908e31256109952b1fdb9b3fa867d2775e1" resolved "https://registry.npmjs.org/for-in/-/for-in-0.1.8.tgz#d8773908e31256109952b1fdb9b3fa867d2775e1"
@ -13937,11 +13942,6 @@ thenify-all@^1.0.0:
dependencies: dependencies:
any-promise "^1.0.0" any-promise "^1.0.0"
throttle-debounce@^2.3.0:
version "2.3.0"
resolved "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-2.3.0.tgz#fd31865e66502071e411817e241465b3e9c372e2"
integrity sha512-H7oLPV0P7+jgvrk+6mwwwBDmxTaxnu9HMXmloNLXwnNO0ZxZ31Orah2n8lU1eMPvsaowP2CX+USCgyovXfdOFQ==
through2-filter@^3.0.0: through2-filter@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz#700e786df2367c2c88cd8aa5be4cf9c1e7831254" resolved "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz#700e786df2367c2c88cd8aa5be4cf9c1e7831254"