Commit 87f5e91a authored by 张烨's avatar 张烨

feat: add proxy and api test page

parent 6c53cf39
......@@ -21,7 +21,7 @@
"prebuild": "npm run build:clean",
"build": "cross-env NODE_ENV=production webpack --config internals/webpack/webpack.prod.babel.js --color -p --progress --hide-modules --display-optimization-bailout",
"build:clean": "rimraf ./build",
"start": "cross-env NODE_ENV=development node server",
"start": "cross-env NODE_ENV=development node --inspect=9229 server",
"start:tunnel": "cross-env NODE_ENV=development ENABLE_TUNNEL=true node server",
"start:production": "npm run test && npm run build && npm run start:prod",
"start:prod": "cross-env NODE_ENV=production node server",
......
......@@ -2,6 +2,7 @@ const path = require('path');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require('webpack-hot-middleware');
const proxy = require('express-http-proxy');
function createWebpackMiddleware(compiler, publicPath) {
return webpackDevMiddleware(compiler, {
......@@ -25,6 +26,39 @@ module.exports = function addDevMiddlewares(app, webpackConfig) {
// Since webpackDevMiddleware uses memory-fs internally to store build
// artifacts, we use it instead
const fs = middleware.fileSystem;
let proxyHost = 'localhost:3002';
app.get('/setproxy', (req, res) => {
if (req.query && req.query.host) {
proxyHost = req.query.host;
res.send(200);
} else {
res.send(400);
}
});
app.use(
'/proxy/*',
proxy(() => proxyHost, {
proxyReqPathResolver: req => {
const parts = req.originalUrl.split('?');
const queryString = parts[1];
const updatedPath = parts[0].replace(/\/proxy\//, '/');
return updatedPath + (queryString ? `?${queryString}` : '');
},
proxyReqOptDecorator: proxyReqOpts => {
// you can update headers
proxyReqOpts.headers.origin = proxyHost;
// proxyReqOpts.headers.Accept = '*/*';
// proxyReqOpts.headers['cookie'] =
// 'UM_distinctid=17543f1ba9ea0a-05f0523c41099d-c781f38-1fa400-17543f1ba9f59; CNZZDATA1278602214=833011536-1603161215-%7C1603161215';
// proxyReqOpts.headers['Accept-Encoding'] = 'gzip, deflate, br';
// proxyReqOpts.headers['Accept-Language'] =
// 'zh-CN,zh;q=0.9,zh-TW;q=0.8,en-US;q=0.7,en;q=0.6';
// console.log({ proxyReqOpts });
// you can change the method
return proxyReqOpts;
},
}),
);
app.get('*', (req, res) => {
fs.readFile(path.join(compiler.outputPath, 'index.html'), (err, file) => {
......
// /* eslint-disable*/
import React, { useState } from 'react';
import { Form, Input, Button, Space, Select } from 'antd';
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
import { get, post } from 'services';
const methods = [
{ label: 'get', value: 'get' },
{ label: 'post', value: 'post' },
];
const RequestTest = () => {
const [protocal, setprotocal] = useState('http://');
const [form] = Form.useForm();
const onFinish = values => {
const url = protocal + values.url;
const methodMap = { get, post };
const { method } = values;
const params =
values.params &&
values.params.reduce((param, { key, value }) => {
param[key.trim()] = value.trim();
return param;
}, {});
if (window.location.host === values.url) {
methodMap[method](url, params).catch(err => {
console.error(err);
});
} else {
get('/setproxy', { host: url.match(/^https?:\/\/([^\/]+)/)[1] }).then(
res => {
const proxyUrl = url.replace(/^https?:\/\/([^\/]+)/, $1 => {
return `${window.location.origin}/proxy`;
});
methodMap[method](proxyUrl, params)
.catch(err => {
console.error(err);
});
},
);
}
};
const handleProtoSelectChange = v => {
setprotocal(v);
};
const handleKeyChange = (v, field) => {
console.log(form);
};
const handleValueChange = (v, field) => {
console.log(form);
};
const protocalBefore = (
<Select
defaultValue="http://"
className="select-before"
onChange={handleProtoSelectChange}
>
<Option value="http://">http://</Option>
<Option value="https://">https://</Option>
</Select>
);
return (
<Form
form={form}
name="dynamic_form_nest_item"
onFinish={onFinish}
autoComplete="off"
>
<Form.Item
name="url"
label="url"
rules={[{ required: true, message: 'Missing url' }]}
initialValue={window.location.host}
>
<Input addonBefore={protocalBefore} />
</Form.Item>
<Form.Item
name="method"
label="method"
rules={[{ required: true, message: 'Missing method' }]}
initialValue="get"
>
<Select options={methods} />
</Form.Item>
<Form.List name="params">
{(fields, { add, remove }) => {
return (
<>
{fields.map(field => (
<Space key={field.key} align="start">
<Form.Item
{...field}
label={`key`}
name={[field.name, 'key']}
fieldKey={[field.fieldKey, 'key']}
rules={[{ required: true, message: 'Missing key' }]}
>
<Input onChange={v => handleKeyChange(v, field)} />
</Form.Item>
<Form.Item
{...field}
label={`value`}
fieldKey={[field.fieldKey, 'value']}
name={[field.name, 'value']}
>
<Input onChange={v => handleValueChange(v, field)} />
</Form.Item>
<MinusCircleOutlined
onClick={() => console.log(field) || remove(field.name)}
/>
</Space>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
block
>
<PlusOutlined /> Add params
</Button>
</Form.Item>
</>
);
}}
</Form.List>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
);
};
export default RequestTest;
......@@ -2,6 +2,7 @@ import UserLayout from '../layouts/UserLayout';
import Login from '../pages/user/login';
import BasicLayout from '../layouts/BasicLayout';
import Welcome from '../pages/Welcome';
import RequestTest from '../pages/testPages/request';
export default {
routes: [
{
......@@ -15,6 +16,17 @@ export default {
},
],
},
{
path: '/test',
component: UserLayout,
routes: [
{
name: '接口测试页',
path: '/test/request',
component: RequestTest,
},
],
},
{
path: '/',
component: BasicLayout,
......
import { request } from '../utils/request';
const axios = request.defaults;
const get = async (url, params, options = {}) =>
request({
url,
method: 'get',
params,
...options,
});
const post = async (url, params, options = {}) =>
request({
url,
params,
method: 'post',
...options,
});
export { get, post }
......@@ -5,7 +5,8 @@ import toLower from 'lodash/toLower';
// import isArray from 'lodash/isArray';
// import isArrayLikeObject from 'lodash/isArrayLikeObject';
import isFunction from 'lodash/isFunction';
import extendConfig from './defaultConfig';
// import extendConfig from './defaultConfig';
const globalConfig = {}
const globalConfigMatchers = [];
/* no-unused-vars */
const getMatchedConfig = requestConfig => {
......@@ -14,13 +15,18 @@ const getMatchedConfig = requestConfig => {
}
for (let i = 0, len = globalConfigMatchers.length; i < len; i += 1) {
const [matcher, extendConfig] = globalConfigMatchers[i];
// 匹配到第一个matcher就返回了, 存在后续配置不生效的情况
if (matcher(requestConfig)) {
console.log(`matched url: ${requestConfig.url}`)
return extendConfig;
}
return null;
}
};
axios.defaults.baseURL = window.location.origin;
export const request = (config, ctx) => {
const {
url,
......@@ -118,10 +124,13 @@ export const request = (config, ctx) => {
request.defaults = axios.defaults;
request.use = (matcher, extendConfig) => {
if (isFunction(matcher)) {
globalConfigMatchers.push([matcher, extendConfig]);
request.use = (matcherOrConfig, extendConfig) => {
if (isFunction(matcherOrConfig)) {
// 如果是函数,则把第二个参数作为满足matcher函数的配置,
// 换言之,只在matcher执行返回true时,才应用extendConfig
globalConfigMatchers.push([matcherOrConfig, extendConfig]);
} else {
globalConfig = assign({}, globalConfig, matcher);
// 如果不是函数,则matcher就是一个配置对象,放到全局配置中
globalConfig = assign({}, globalConfig, matcherOrConfig);
}
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment