diff --git a/client/components/Breadcrumb/Breadcrumb.scss b/client/components/Breadcrumb/Breadcrumb.scss index c0b73ff6..08ec4d76 100644 --- a/client/components/Breadcrumb/Breadcrumb.scss +++ b/client/components/Breadcrumb/Breadcrumb.scss @@ -2,5 +2,5 @@ .breadcrumb-container { @include row-width-limit; - margin: 16px auto -8px; + margin: 16px auto; } diff --git a/client/containers/AddInterface/AddInterface.js b/client/containers/AddInterface/AddInterface.js index b9e3e6d8..1e0e333b 100644 --- a/client/containers/AddInterface/AddInterface.js +++ b/client/containers/AddInterface/AddInterface.js @@ -78,7 +78,7 @@ class AddInterface extends Component { mockJson: '', mockURL: '', projectData: {}, - tagName: '创建接口', + tagName: '添加接口', showMock: '' } } diff --git a/client/containers/AddInterface/InterfaceTest/InterfaceTest.js b/client/containers/AddInterface/InterfaceTest/InterfaceTest.js index 1c0f1cae..43664fe7 100644 --- a/client/containers/AddInterface/InterfaceTest/InterfaceTest.js +++ b/client/containers/AddInterface/InterfaceTest/InterfaceTest.js @@ -1,10 +1,11 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' import { connect } from 'react-redux' -import { Button, Input, Select, Card, Alert, Spin } from 'antd' +import { Button, Input, Select, Card, Alert, Spin, Icon, message } from 'antd' import { autobind } from 'core-decorators'; import crossRequest from 'cross-request'; import { withRouter } from 'react-router'; +import axios from 'axios'; import URL from 'url'; import { @@ -48,8 +49,8 @@ export default class InterfaceTest extends Component { params: {}, paramsNotJson: false, headers: {}, - search: '', - currDomain: '' + currDomain: '', + paramsType: 'from' } constructor(props) { @@ -57,15 +58,15 @@ export default class InterfaceTest extends Component { } componentWillMount() { - this.interfacePropsToState() + this.getInterfaceState() } componentWillReceiveProps(nextProps) { - this.interfacePropsToState(nextProps) + this.getInterfaceState(nextProps) } @autobind - interfacePropsToState(nextProps) { + getInterfaceState(nextProps) { const props = nextProps || this.props; const { method, url, seqGroup, interfaceProject } = props; const { prd_host, basepath, protocol, env } = interfaceProject; @@ -76,33 +77,40 @@ export default class InterfaceTest extends Component { domains[item.name] = item.domain; }) - const query = {}; - let params = {}; + const query = []; + let params = []; let reqParams = this.props.reqParams ? this.props.reqParams : '{}'; let paramsNotJson = false; try { - reqParams = JSON.parse(reqParams) - paramsNotJson = false; + reqParams = JSON.parse(reqParams); + // paramsNotJson = false; } catch (e) { - paramsNotJson = true; + // paramsNotJson = true; + reqParams = {}; + message.error('请求参数不是 JSON 格式'); } if (method === 'GET') { Object.keys(reqParams).forEach(key => { const value = typeof reqParams[key] === 'object' ? JSON.stringify(reqParams[key]) : reqParams[key].toString(); - query[key] = value; + query.push({key, value}) }) } else if (method === 'POST') { - params = reqParams; + // params = reqParams; + Object.keys(reqParams).forEach(key => { + const value = typeof reqParams[key] === 'object' ? JSON.stringify(reqParams[key]) : reqParams[key].toString(); + query.push({key, value, type: 'text'}) + }) } - const headers = {} + const headers = [] seqGroup.forEach((headerItem) => { if (headerItem.name) { - headers[headerItem.name] = headerItem.value; + headers.push({name: headerItem.name, value: headerItem.value}); } }) this.setState({ + method, domains, pathname, query, @@ -110,21 +118,21 @@ export default class InterfaceTest extends Component { paramsNotJson, headers, currDomain: domains.prd, - loading: false + loading: false, + paramsType: 'form' }); } @autobind - testInterface() { - const { method } = this.props; - const { pathname, query, headers, params, currDomain } = this.state; + requestInterface() { + const { headers, params, currDomain, method, pathname, query } = this.state; const urlObj = URL.parse(currDomain); const href = URL.format({ protocol: urlObj.protocol || 'http', host: urlObj.host, pathname, - query + query: this.getQueryObj(query) }); this.setState({ loading: true }) @@ -132,8 +140,8 @@ export default class InterfaceTest extends Component { crossRequest({ url: href, method, - headers, - data: params, + headers: this.getHeadersObj(headers), + data: this.arrToObj(params), success: (res) => { try { res = JSON.parse(res) @@ -152,44 +160,149 @@ export default class InterfaceTest extends Component { @autobind changeDomain(value) { - const domain = this.state.domains[value]; - this.setState({ currDomain: domain }); + this.setState({ currDomain: value }); } @autobind - changeHeader(e, key) { + selectDomain(value) { + this.setState({ currDomain: value }); + } + + @autobind + changeHeader(e, index, isName) { const headers = JSON.parse(JSON.stringify(this.state.headers)); - headers[key] = e.target.value; + const v = e.target.value; + if (isName) { + headers[index].name = v; + } else { + headers[index].value = v; + } this.setState({ headers }); } - @autobind - changeQuery(e, key) { - const query = JSON.parse(JSON.stringify(this.state.query)); - query[key] = e.target.value; - this.setState({ query }); + addHeader() { + const { headers } = this.state; + this.setState({headers: headers.concat([{name: '', value: ''}])}) + } + @autobind + deleteHeader(index) { + const { headers } = this.state; + this.setState({headers: headers.filter((item, i) => +index !== +i)}); } @autobind - changeParams(e, key) { + changeQuery(e, index, isKey) { + const query = JSON.parse(JSON.stringify(this.state.query)); + const v = e.target.value; + if (isKey) { + query[index].key = v; + } else { + query[index].value = v; + } + this.setState({ query }); + } + @autobind + addQuery() { + const { query } = this.state; + this.setState({query: query.concat([{key: '', value: ''}])}) + } + @autobind + deleteQuery(index) { + const { query } = this.state; + this.setState({query: query.filter((item, i) => +index !== +i)}); + } + + @autobind + changeParams(e, index, type) { const params = JSON.parse(JSON.stringify(this.state.params)); - params[key] = e.target.value; + switch (type) { + case 'key': + params[index].key = e.target.value + break; + case 'type': + params[index].type = e + break; + case 'value': + params[index].value = e.target.value + break; + default: + break; + } this.setState({ params }); } + @autobind + addParams() { + const { params } = this.state; + this.setState({params: params.concat([{key: '', value: '', type: 'text'}])}) + } + @autobind + deleteParams(index) { + const { params } = this.state; + this.setState({params: params.filter((item, i) => +index !== +i)}); + } + + @autobind + changeMethod(value) { + this.setState({ method: value }); + } + + @autobind + changePath(e) { + const path = e.target.value; + const urlObj = URL.parse(path, true); + this.setState({ + query: urlObj.query, + pathname: urlObj.pathname + }) + } + + @autobind + changeParamsType(value) { + this.setState({paramsType: value}) + } hasCrossRequestPlugin() { const dom = document.getElementById('y-request'); return dom.getAttribute('key') === 'yapi'; } + arrToObj(arr) { + const obj = {}; + arr.forEach(item => { + if (item.key) { + obj[item.key] = item.value || ''; + } + }) + return obj; + } + getQueryObj(query) { + const queryObj = {}; + query.forEach(item => { + if (item.key) { + queryObj[item.key] = item.value || ''; + } + }) + return queryObj; + } + getHeadersObj(headers) { + const headersObj = {}; + headers.forEach(item => { + if (item.name && item.value) { + headersObj[item.name] = item.value; + } + }) + return headersObj; + } + render () { - const { interfaceName, method } = this.props; - const { domains, pathname, query, headers, params, paramsNotJson } = this.state; - const search = URL.format({ - query - }); + const { interfaceName } = this.props; + const { method, domains, pathname, query, headers, params, currDomain, paramsType } = this.state; const hasPlugin = this.hasCrossRequestPlugin(); + const search = decodeURIComponent(URL.format({query: this.getQueryObj(query)})); + + console.log(axios) + window.axios = axios return ( @@ -213,6 +326,8 @@ export default class InterfaceTest extends Component { }
{interfaceName}
+ + {/* url */}
@@ -221,68 +336,110 @@ export default class InterfaceTest extends Component { - - + + + + - + (请求测试真实接口)
- + + + { + query.map((item, index) => { + return ( +
+ this.changeQuery(e, index, true)} style={{display: 'inline-block', width: 200, margin: 10}} />{' = '} + this.changeQuery(e, index)} style={{display: 'inline-block', width: 200, margin: 10}} /> + this.deleteQuery(index)} /> +
+ ) + }) + } + +
+
{ - Object.keys(headers).map((key, index) => { + headers.map((item, index) => { return (
- {' = '} - this.changeHeader(e, key)} style={{display: 'inline-block', width: 200, margin: 10}} /> + this.changeHeader(e, index, true)} style={{display: 'inline-block', width: 200, margin: 10}} />{' = '} + this.changeHeader(e, index)} style={{display: 'inline-block', width: 200, margin: 10}} /> + this.deleteHeader(index)} />
) }) } +
- -
- { - Object.keys(query).map((key, index) => { - const value = typeof query[key] === 'object' ? JSON.stringify(query[key]) : query[key].toString(); - return ( -
- {' = '} - this.changeQuery(e, key)} style={{display: 'inline-block', width: 200, margin: 10}} /> -
- ) - }) - } -
-
- +
- { paramsNotJson ? - : - Object.keys(params).map((key, index) => { - const value = typeof params[key] === 'object' ? JSON.stringify(params[key]) : params[key].toString(); - return ( -
- {' = '} - this.changeParams(e, key)} style={{display: 'inline-block', width: 200, margin: 10}} /> -
- ) - }) +
+ +
+ { method === 'POST' && paramsType !== 'form' && paramsType !== 'file' && +
+ +
{paramsType}
+
+ } + { + method === 'POST' && paramsType === 'form' && ( +
+ { + params.map((item, index) => { + return ( +
+ this.changeParams(e, index, 'key')} style={{display: 'inline-block', width: 200, margin: 10}} /> + []{' = '} + {item.type === 'file' ? + : + this.changeParams(e, index, 'value')} style={{display: 'inline-block', width: 200, margin: 10}} /> + } +
+ ) + }) + } + +
+ ) + } + { + method === 'POST' && paramsType === 'file' && ( +
+ +
+ ) + } + { + method !== 'POST' && ( +
GET 请求没有 Body。
+ ) }
diff --git a/client/containers/AddInterface/InterfaceTest/InterfaceTest.scss b/client/containers/AddInterface/InterfaceTest/InterfaceTest.scss index 14350c6c..a5f2179f 100644 --- a/client/containers/AddInterface/InterfaceTest/InterfaceTest.scss +++ b/client/containers/AddInterface/InterfaceTest/InterfaceTest.scss @@ -3,7 +3,16 @@ font-size: 24px; margin-bottom: 24px; } - .req-row { - margin-bottom: 24px; + .icon-btn { + cursor: pointer; + margin-left: 6px; + } + .icon-btn:hover { + color: #108ee9; } } +.floatfix:after{ + content:""; + display:table; + clear:both; +} diff --git a/server/middleware/mockServer.js b/server/middleware/mockServer.js index e6389063..22637a72 100644 --- a/server/middleware/mockServer.js +++ b/server/middleware/mockServer.js @@ -16,7 +16,6 @@ module.exports = async (ctx, next) => { yapi.commons.log('MockServer Running...'); let projectInst = yapi.getInst(projectModel), projects; - try { projects = await projectInst.getByDomain(hostname); } catch (e) { @@ -46,9 +45,15 @@ module.exports = async (ctx, next) => { let interfaceInst = yapi.getInst(interfaceModel); try { - interfaceData = await interfaceInst.getByPath(project._id, ctx.path.substr(project.basepath.length)); + interfaceData = await interfaceInst.getByPath(project._id, ctx.path.substr(project.basepath.length), ctx.method); if (!interfaceData || interfaceData.length === 0) { + //非正常跨域预检请求回应 + if(ctx.method === 'OPTIONS'){ + ctx.set("Access-Control-Allow-Origin", "*") + ctx.set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE") + return ctx.body = 'ok' + } return ctx.body = yapi.commons.resReturn(null, 404, '不存在的api'); } @@ -57,7 +62,7 @@ module.exports = async (ctx, next) => { } interfaceData = interfaceData[0]; - + ctx.set("Access-Control-Allow-Origin", "*") if (interfaceData.res_body_type === 'json') { return ctx.body = Mock.mock( yapi.commons.json_parse(interfaceData.res_body) diff --git a/server/models/base.js b/server/models/base.js index 83553958..a2651393 100644 --- a/server/models/base.js +++ b/server/models/base.js @@ -16,7 +16,7 @@ class baseModel{ model: this.name, field: this.getPrimaryKey(), startAt: 101, - incrementBy: yapi.commons.rand(1, 100) + incrementBy: yapi.commons.rand(1, 10) }); } diff --git a/server/models/interface.js b/server/models/interface.js index f5c4e1c9..e71ad853 100644 --- a/server/models/interface.js +++ b/server/models/interface.js @@ -47,10 +47,11 @@ class interfaceModel extends baseModel { .exec(); } - getByPath(project_id, path) { + getByPath(project_id, path, method) { return this.model.find({ project_id: project_id, - path: path + path: path, + method: method }) .exec(); } diff --git a/server_dist/middleware/mockServer.js b/server_dist/middleware/mockServer.js index 14cd95ef..2658e80d 100644 --- a/server_dist/middleware/mockServer.js +++ b/server_dist/middleware/mockServer.js @@ -103,51 +103,62 @@ module.exports = function () { interfaceInst = _yapi2.default.getInst(_interface2.default); _context.prev = 25; _context.next = 28; - return interfaceInst.getByPath(project._id, ctx.path.substr(project.basepath.length)); + return interfaceInst.getByPath(project._id, ctx.path.substr(project.basepath.length), ctx.method); case 28: interfaceData = _context.sent; if (!(!interfaceData || interfaceData.length === 0)) { - _context.next = 31; + _context.next = 35; break; } + if (!(ctx.method === 'OPTIONS')) { + _context.next = 34; + break; + } + + ctx.set("Access-Control-Allow-Origin", "*"); + ctx.set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE"); + return _context.abrupt('return', ctx.body = 'ok'); + + case 34: return _context.abrupt('return', ctx.body = _yapi2.default.commons.resReturn(null, 404, '不存在的api')); - case 31: + case 35: if (!(interfaceData.length > 1)) { - _context.next = 33; + _context.next = 37; break; } return _context.abrupt('return', ctx.body = _yapi2.default.commons.resReturn(null, 405, '存在多个api,请检查数据库')); - case 33: + case 37: interfaceData = interfaceData[0]; + ctx.set("Access-Control-Allow-Origin", "*"); if (!(interfaceData.res_body_type === 'json')) { - _context.next = 36; + _context.next = 41; break; } return _context.abrupt('return', ctx.body = _mockjs2.default.mock(_yapi2.default.commons.json_parse(interfaceData.res_body))); - case 36: + case 41: return _context.abrupt('return', ctx.body = interfaceData.res_body); - case 39: - _context.prev = 39; + case 44: + _context.prev = 44; _context.t1 = _context['catch'](25); return _context.abrupt('return', ctx.body = _yapi2.default.commons.resReturn(null, 409, _context.t1.message)); - case 42: + case 47: case 'end': return _context.stop(); } } - }, _callee, undefined, [[10, 16], [25, 39]]); + }, _callee, undefined, [[10, 16], [25, 44]]); })); return function (_x, _x2) { diff --git a/server_dist/models/base.js b/server_dist/models/base.js index 62d976e5..2680baef 100644 --- a/server_dist/models/base.js +++ b/server_dist/models/base.js @@ -38,7 +38,7 @@ var baseModel = function () { model: this.name, field: this.getPrimaryKey(), startAt: 101, - incrementBy: _yapi2.default.commons.rand(1, 100) + incrementBy: _yapi2.default.commons.rand(1, 10) }); } diff --git a/server_dist/models/interface.js b/server_dist/models/interface.js index 7e7b1ef6..aa3165c0 100644 --- a/server_dist/models/interface.js +++ b/server_dist/models/interface.js @@ -88,10 +88,11 @@ var interfaceModel = function (_baseModel) { } }, { key: 'getByPath', - value: function getByPath(project_id, path) { + value: function getByPath(project_id, path, method) { return this.model.find({ project_id: project_id, - path: path + path: path, + method: method }).exec(); } }, {