Skip to content

Commit

Permalink
feat: 增加 mock 接口请求字段参数验证
Browse files Browse the repository at this point in the history
  • Loading branch information
gaoxiaomumu committed Aug 7, 2018
1 parent aa2ff8f commit ba05e58
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 11 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

* json schema number和integer支持枚举
* 服务端测试增加下载功能
* 增加 mock 接口请求字段参数验证

### Bug Fixed

Expand Down
3 changes: 2 additions & 1 deletion client/containers/Project/Interface/InterfaceList/View.js
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,8 @@ class View extends Component {
Mock地址:
</Col>
<Col span={18} className="colValue">
{this.props.currProject.is_mock_open && <span>(全局mock已开启) </span>}
{this.props.currProject.is_mock_open ? <span>( 全局mock </span> : <span>( </span>}
{this.props.currProject.strice ? <span> & 严格模式 ) </span> : <span>) </span>}
<span
className="href"
onClick={() =>
Expand Down
21 changes: 18 additions & 3 deletions client/containers/Project/Setting/ProjectMessage/ProjectMessage.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,6 @@ class ProjectMessage extends Component {
const selectGroup = _.find(groupList, item => {
return item._id == group_id;
});

updateProject(assignValue)
.then(res => {
if (res.payload.data.errcode == 0) {
Expand Down Expand Up @@ -222,8 +221,8 @@ class ProjectMessage extends Component {
(location.port !== '' ? ':' + location.port : '') +
`/mock/${projectMsg._id}${projectMsg.basepath}+$接口请求路径`;
let initFormValues = {};
const { name, basepath, desc, project_type, group_id, switch_notice } = projectMsg;
initFormValues = { name, basepath, desc, project_type, group_id, switch_notice };
const { name, basepath, desc, project_type, group_id, switch_notice, strice } = projectMsg;
initFormValues = { name, basepath, desc, project_type, group_id, switch_notice, strice };

const colorArr = entries(constants.PROJECT_COLOR);
const colorSelector = (
Expand Down Expand Up @@ -359,6 +358,22 @@ class ProjectMessage extends Component {
]
})(<TextArea rows={8} />)}
</FormItem>
<FormItem
{...formItemLayout}
label={
<span>
mock严格模式&nbsp;
<Tooltip title="开启mock严格模式后会对query,body form 的必须字段和json schema 进行校验">
<Icon type="question-circle-o" />
</Tooltip>
</span>
}
>
{getFieldDecorator('strice', {
valuePropName: 'checked',
initialValue: initFormValues.strice
})(<Switch checkedChildren="开" unCheckedChildren="关" />)}
</FormItem>
<FormItem {...formItemLayout} label="默认开启邮件通知">
{getFieldDecorator('switch_notice', {
valuePropName: 'checked',
Expand Down
5 changes: 3 additions & 2 deletions client/reducer/modules/project.js
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ export function addProject(data) {

// 修改项目
export function updateProject(data) {
const { name, project_type, basepath, desc, _id, env, group_id, switch_notice } = data;
const { name, project_type, basepath, desc, _id, env, group_id, switch_notice, strice } = data;
const param = {
name,
project_type,
Expand All @@ -214,7 +214,8 @@ export function updateProject(data) {
desc,
id: _id,
env,
group_id
group_id,
strice
};
return {
type: PROJECT_UPDATE,
Expand Down
72 changes: 69 additions & 3 deletions server/middleware/mockServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,60 @@ function handleCorsRequest(ctx) {
ctx.set('Access-Control-Max-Age', 1728000);
ctx.body = 'ok';
}
// 必填字段是否填写好
function mockValidator(interfaceData, ctx) {
let i,
j,
l,
len,
noRequiredArr = [];
// query 判断
for (i = 0, l = interfaceData.req_query.length; i < l; i++) {
let curQuery = interfaceData.req_query[i];

if (curQuery && typeof curQuery === 'object' && curQuery.required === '1') {
if (!ctx.query[curQuery.name]) {
noRequiredArr.push(curQuery.name);
}
}
}
// form 表单判断
if (interfaceData.req_body_type === 'form') {
for (j = 0, len = interfaceData.req_body_form.length; j < len; j++) {
let curForm = interfaceData.req_body_form[j];
if (curForm && typeof curForm === 'object' && curForm.required === '1') {
if (
ctx.request.body[curForm.name] ||
(ctx.request.body.fields && ctx.request.body.fields[curForm.name]) ||
(ctx.request.body.files && ctx.request.body.files[curForm.name])
) {
continue;
}
noRequiredArr.push(curForm.name);
}
}
}
let validResult;
// json schema 判断
if (interfaceData.req_body_type === 'json' && interfaceData.res_body_is_json_schema === true) {
const schema = yapi.commons.json_parse(interfaceData.req_body_other);
const params = yapi.commons.json_parse(ctx.request.body);
validResult = yapi.commons.schemaValidator(schema, params);
}

if (noRequiredArr.length > 0 || (validResult && !validResult.valid)) {
let message = `错误信息: `;
message += noRequiredArr.length > 0 ? `缺少必须字段 ${noRequiredArr.join(',')}` : '';
message += validResult && !validResult.valid ? `shema 验证 ${validResult.message}` : '';

return {
valid: false,
message
};
}

return { valid: true };
}

module.exports = async (ctx, next) => {
// no used variable 'hostname' & 'config'
Expand Down Expand Up @@ -118,10 +172,9 @@ module.exports = async (ctx, next) => {
newpath = path.substr(project.basepath.length);
interfaceData = await interfaceInst.getByPath(project._id, newpath, ctx.method);

//处理query_path情况
//处理query_path情况 url 中有 ?params=xxx
if (!interfaceData || interfaceData.length === 0) {
interfaceData = await interfaceInst.getByQueryPath(project._id, newpath, ctx.method);

let i,
l,
j,
Expand All @@ -143,6 +196,7 @@ module.exports = async (ctx, next) => {
match = true;
}
}

if (match) {
interfaceData = [currentInterfaceData];
break;
Expand All @@ -156,7 +210,6 @@ module.exports = async (ctx, next) => {
//处理动态路由
if (!interfaceData || interfaceData.length === 0) {
let newData = await interfaceInst.getVar(project._id, ctx.method);

let findInterface = _.find(newData, item => {
let m = matchApi(newpath, item.path);
if (m !== false) {
Expand Down Expand Up @@ -188,9 +241,22 @@ module.exports = async (ctx, next) => {
interfaceData = interfaceData[0];
}

// 必填字段是否填写好
if (project.strice) {
const validResult = mockValidator(interfaceData, ctx);
if (!validResult.valid) {
return (ctx.body = yapi.commons.resReturn(
null,
404,
`接口字段验证不通过, ${validResult.message}`
));
}
}

ctx.set('Access-Control-Allow-Origin', '*');
let res;

// mock 返回值处理
res = interfaceData.res_body;
try {
if (interfaceData.res_body_type === 'json') {
Expand Down
5 changes: 3 additions & 2 deletions server/models/project.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ class projectModel extends baseModel {
pre_script: String,
after_script: String,
project_mock_script: String,
is_mock_open: { type: Boolean, default: false }
is_mock_open: { type: Boolean, default: false },
strice: { type: Boolean, default: false }
};
}

Expand Down Expand Up @@ -82,7 +83,7 @@ class projectModel extends baseModel {
getBaseInfo(id, select) {
select =
select ||
'_id uid name basepath switch_notice desc group_id project_type env icon color add_time up_time pre_script after_script project_mock_script is_mock_open';
'_id uid name basepath switch_notice desc group_id project_type env icon color add_time up_time pre_script after_script project_mock_script is_mock_open strice';
return this.model
.findOne({
_id: id
Expand Down
22 changes: 22 additions & 0 deletions server/utils/commons.js
Original file line number Diff line number Diff line change
Expand Up @@ -616,3 +616,25 @@ exports.handleMockScript = function(script, context) {
context.httpCode = sandbox.httpCode;
context.delay = sandbox.delay;
};

// json schema 验证器
exports.schemaValidator = function(schema, params) {
const ajv = new Ajv({
format: false
});

var localize = require('ajv-i18n');
const validate = ajv.compile(schema);
let valid = validate(params);

let message = '请求参数 ';
if (!valid) {
localize.zh(validate.errors);
message += ajv.errorsText(validate.errors, { separator: '\n' });
}

return {
valid: valid,
message: message
};
};

0 comments on commit ba05e58

Please sign in to comment.