mirror of
https://github.com/YMFE/yapi.git
synced 2025-04-06 15:00:26 +08:00
feat: 增加返回数据验证
This commit is contained in:
parent
ab8d9ce127
commit
ef42bb7156
@ -3,6 +3,7 @@
|
||||
* json schema number和integer支持枚举
|
||||
* 服务端测试增加下载功能
|
||||
* 增加 mock 接口请求字段参数验证
|
||||
* 增加返回数据验证
|
||||
|
||||
### Bug Fixed
|
||||
|
||||
|
@ -13,19 +13,20 @@ import {
|
||||
Tabs,
|
||||
Switch,
|
||||
Row,
|
||||
Col
|
||||
Col,
|
||||
Alert
|
||||
} from 'antd';
|
||||
import constants from '../../constants/variable.js';
|
||||
import AceEditor from 'client/components/AceEditor/AceEditor';
|
||||
import _ from 'underscore';
|
||||
import { isJson, deepCopyJson } from '../../common.js';
|
||||
import { isJson, deepCopyJson, json5_parse } from '../../common.js';
|
||||
import axios from 'axios';
|
||||
import ModalPostman from '../ModalPostman/index.js';
|
||||
import CheckCrossInstall, { initCrossRequest } from './CheckCrossInstall.js';
|
||||
import './Postman.scss';
|
||||
import ProjectEnv from '../../containers/Project/Setting/ProjectEnv/index.js';
|
||||
import json5 from 'json5';
|
||||
const { handleParamsValue, ArrayToObject } = require('common/utils.js');
|
||||
const { handleParamsValue, ArrayToObject, schemaValidator } = require('common/utils.js');
|
||||
const {
|
||||
handleParams,
|
||||
checkRequestBodyIsRaw,
|
||||
@ -264,7 +265,7 @@ export default class Run extends Component {
|
||||
// console.log('global',global);
|
||||
let globalValue = ArrayToObject(global);
|
||||
return handleParamsValue(val, {
|
||||
global:globalValue
|
||||
global: globalValue
|
||||
});
|
||||
}
|
||||
|
||||
@ -295,7 +296,6 @@ export default class Run extends Component {
|
||||
loading: true
|
||||
});
|
||||
|
||||
|
||||
let options = handleParams(this.state, this.handleValue),
|
||||
result;
|
||||
|
||||
@ -335,6 +335,15 @@ export default class Run extends Component {
|
||||
res_body_type: 'json'
|
||||
});
|
||||
}
|
||||
|
||||
// 对返回值数据结构和定义的返回数据结构进行 格式校验
|
||||
let validResult = this.resBodyValidator(this.props.data, result.body);
|
||||
if (!validResult.valid) {
|
||||
this.setState({ test_valid_msg: `返回参数 ${validResult.message}` });
|
||||
} else {
|
||||
this.setState({ test_valid_msg: '' });
|
||||
}
|
||||
|
||||
this.setState({
|
||||
resStatusCode: result.status,
|
||||
resStatusText: result.statusText,
|
||||
@ -342,6 +351,20 @@ export default class Run extends Component {
|
||||
test_res_body: result.body
|
||||
});
|
||||
};
|
||||
// 返回数据与定义数据的比较判断
|
||||
resBodyValidator = (interfaceData, test_res_body) => {
|
||||
const { res_body_type, res_body_is_json_schema, res_body } = interfaceData;
|
||||
let validResult = { valid: true };
|
||||
|
||||
if (res_body_type === 'json' && res_body_is_json_schema) {
|
||||
const schema = json5_parse(res_body);
|
||||
const params = json5_parse(test_res_body);
|
||||
validResult = schemaValidator(schema, params);
|
||||
}
|
||||
|
||||
console.log(validResult);
|
||||
return validResult;
|
||||
};
|
||||
|
||||
changeParam = (name, v, index, key) => {
|
||||
key = key || 'value';
|
||||
@ -848,6 +871,14 @@ export default class Run extends Component {
|
||||
>
|
||||
{this.state.resStatusCode + ' ' + this.state.resStatusText}
|
||||
</h2>
|
||||
{this.state.test_valid_msg && (
|
||||
<Alert
|
||||
message="Warning"
|
||||
type="warning"
|
||||
showIcon
|
||||
description={this.state.test_valid_msg}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="container-header-body">
|
||||
<div className="header">
|
||||
@ -861,7 +892,7 @@ export default class Run extends Component {
|
||||
readOnly={true}
|
||||
className="pretty-editor-header"
|
||||
data={this.state.test_res_header}
|
||||
mode="json"
|
||||
mode="json"
|
||||
/>
|
||||
</div>
|
||||
<div className="resizer">
|
||||
|
@ -1,6 +1,7 @@
|
||||
const Mock = require('mockjs');
|
||||
const filter = require('./power-string.js').filter;
|
||||
const json5 = require('json5');
|
||||
const Ajv = require('ajv');
|
||||
/**
|
||||
* 作用:解析规则串 key ,然后根据规则串的规则以及路径找到在 json 中对应的数据
|
||||
* 规则串:$.{key}.{body||params}.{dataPath} 其中 body 为返回数据,params 为请求数据,datapath 为数据的路径
|
||||
@ -237,3 +238,40 @@ exports.timeago = function(timestamp) {
|
||||
return '刚刚';
|
||||
}
|
||||
};
|
||||
|
||||
// json schema 验证器
|
||||
exports.schemaValidator = function(schema, params) {
|
||||
try {
|
||||
const ajv = new Ajv({
|
||||
format: false,
|
||||
meta: false,
|
||||
allErrors: true
|
||||
});
|
||||
|
||||
let metaSchema = require('ajv/lib/refs/json-schema-draft-04.json');
|
||||
ajv.addMetaSchema(metaSchema);
|
||||
ajv._opts.defaultMeta = metaSchema.id;
|
||||
|
||||
ajv._refs['http://json-schema.org/schema'] = 'http://json-schema.org/draft-04/schema';
|
||||
|
||||
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
|
||||
};
|
||||
} catch (e) {
|
||||
return {
|
||||
valid: false,
|
||||
message: e.message
|
||||
};
|
||||
}
|
||||
};
|
||||
|
@ -223,7 +223,7 @@ class interfaceColController extends baseController {
|
||||
if ((await this.checkAuth(project._id, 'project', 'view')) !== true) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 406, '没有权限'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let index = 0; index < resultList.length; index++) {
|
||||
let result = resultList[index].toObject();
|
||||
@ -559,9 +559,9 @@ class interfaceColController extends baseController {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, '用例id不能为空'));
|
||||
}
|
||||
|
||||
if (!params.casename) {
|
||||
return (ctx.body = yapi.commons.resReturn(null, 400, '用例名称不能为空'));
|
||||
}
|
||||
// if (!params.casename) {
|
||||
// return (ctx.body = yapi.commons.resReturn(null, 400, '用例名称不能为空'));
|
||||
// }
|
||||
|
||||
let caseData = await this.caseModel.get(params.id);
|
||||
let auth = await this.checkAuth(caseData.project_id, 'project', 'edit');
|
||||
@ -638,6 +638,7 @@ class interfaceColController extends baseController {
|
||||
result.req_params = yapi.commons.handleParamsValue(data.req_params, result.req_params);
|
||||
result.interface_up_time = data.up_time;
|
||||
result.req_body_is_json_schema = data.req_body_is_json_schema;
|
||||
result.res_body_is_json_schema = data.res_body_is_json_schema;
|
||||
ctx.body = yapi.commons.resReturn(result);
|
||||
} catch (e) {
|
||||
ctx.body = yapi.commons.resReturn(null, 400, e.message);
|
||||
|
@ -212,6 +212,26 @@ class interfaceColController extends baseController {
|
||||
ctx.body = yapi.commons.resReturn(null, 402, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试返回值
|
||||
* @interface /test/response
|
||||
* @method get
|
||||
* @return {Object}
|
||||
* @example
|
||||
*/
|
||||
async testResponse(ctx) {
|
||||
try {
|
||||
// let result = `<div><h2>12222222</h2></div>`
|
||||
// let result = `wieieieieiieieie`
|
||||
let result = {a: '12'}
|
||||
ctx.body = result;
|
||||
|
||||
} catch (e) {
|
||||
ctx.body = yapi.commons.resReturn(null, 402, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = interfaceColController;
|
||||
|
@ -2,6 +2,7 @@ const yapi = require('../yapi.js');
|
||||
const projectModel = require('../models/project.js');
|
||||
const interfaceModel = require('../models/interface.js');
|
||||
const mockExtra = require('../../common/mock-extra.js');
|
||||
const { schemaValidator } = require('../../common/utils.js');
|
||||
const _ = require('underscore');
|
||||
const Mock = require('mockjs');
|
||||
/**
|
||||
@ -117,13 +118,13 @@ function mockValidator(interfaceData, ctx) {
|
||||
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);
|
||||
validResult = 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}` : '';
|
||||
message += validResult && !validResult.valid ? `shema 验证请求参数 ${validResult.message}` : '';
|
||||
|
||||
return {
|
||||
valid: false,
|
||||
|
@ -538,6 +538,11 @@ let routerConfig = {
|
||||
action: 'testRaw',
|
||||
path: 'raw',
|
||||
method: 'post'
|
||||
},
|
||||
{
|
||||
action: 'testResponse',
|
||||
path: 'response',
|
||||
method: 'get'
|
||||
}
|
||||
],
|
||||
open: [
|
||||
|
@ -617,39 +617,3 @@ exports.handleMockScript = function(script, context) {
|
||||
context.delay = sandbox.delay;
|
||||
};
|
||||
|
||||
// json schema 验证器
|
||||
exports.schemaValidator = function(schema, params) {
|
||||
try {
|
||||
const ajv = new Ajv({
|
||||
format: false,
|
||||
meta: false,
|
||||
allErrors: true
|
||||
});
|
||||
|
||||
let metaSchema = require('ajv/lib/refs/json-schema-draft-04.json');
|
||||
ajv.addMetaSchema(metaSchema);
|
||||
ajv._opts.defaultMeta = metaSchema.id;
|
||||
|
||||
ajv._refs['http://json-schema.org/schema'] = 'http://json-schema.org/draft-04/schema';
|
||||
|
||||
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
|
||||
};
|
||||
} catch (e) {
|
||||
return {
|
||||
valid: false,
|
||||
message: e.message
|
||||
};
|
||||
}
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user