import React, { Component } from 'react' import PropTypes from 'prop-types' import Mock from 'mockjs' import { Button, Input, Select, Card, Alert, Spin, Icon, Collapse, Radio, Tooltip, message } from 'antd' import { autobind } from 'core-decorators'; import mockEditor from '../../containers/Project/Interface/InterfaceList/mockEditor' import URL from 'url'; const MockExtra = require('common/mock-extra.js') import './Postman.scss'; const { TextArea } = Input; const InputGroup = Input.Group; const Option = Select.Option; const Panel = Collapse.Panel; const RadioButton = Radio.Button; const RadioGroup = Radio.Group; export default class Run extends Component { static propTypes = { data: PropTypes.object, save: PropTypes.func, saveTip: PropTypes.string, type: PropTypes.string } state = { res: null, resHeader: null, method: 'GET', domains: [], pathname: '', query: [], bodyForm: [], headers: [], caseEnv: '', bodyType: '', bodyOther: '', loading: false, validRes: null, hasPlugin: true } constructor(props) { super(props) } componentWillMount() { let startTime = 0; this.interval = setInterval(()=>{ startTime += 500; if(startTime > 5000){ clearInterval(this.interval); } if(window.crossRequest){ clearInterval(this.interval); this.setState({ hasPlugin: true }) }else{ this.setState({ hasPlugin: false }) } }, 500) this.getInterfaceState() } componentWillUnmount(){ clearInterval(this.interval) } componentWillReceiveProps(nextProps) { if (nextProps.data._id !== this.props.data._id) { this.getInterfaceState(nextProps) } } componentDidMount() { const { bodyType } = this.state; if(bodyType && bodyType !== 'file' && bodyType !== 'form') { this.loadBodyEditor() } } @autobind getInterfaceState(nextProps) { const props = nextProps || this.props; const { data, type } = props; const { method = '', path: url = '', req_headers = [], req_body_type, req_query = [], req_params = [], req_body_other = '', req_body_form = [], basepath = '', env = [], case_env = '' } = data; // case 任意编辑 pathname,不管项目的 basepath const pathname = (type === 'inter' ? (basepath + url) : url).replace(/\/+/g, '/'); // let hasContentType = false; // req_headers.forEach(headerItem => { // // TODO 'Content-Type' 排除大小写不同格式影响 // if (headerItem.name === 'Content-Type'){ // hasContentType = true; // headerItem.value = headerItem.value || 'application/x-www-form-urlencoded'; // } // }) // if (!hasContentType) { // req_headers.push({name: 'Content-Type', value: 'application/x-www-form-urlencoded'}); // } // const domains = env.concat(); // if (domain && !env.find(item => item.domain === domain)) { // domains.push({name: 'default', domain}) // } this.setState({ method, domains: env.concat(), pathParam: req_params.concat(), pathname, query: req_query.concat(), bodyForm: req_body_form.concat(), headers: req_headers.concat(), bodyOther: req_body_other, caseEnv: case_env || (env[0] && env[0].name), bodyType: req_body_type || 'form', loading: false }, () => { if(req_body_type && req_body_type !== 'file' && req_body_type !== 'form') { this.loadBodyEditor() } }); } @autobind reqRealInterface() { if (this.state.loading) { this.setState({ loading: false }) return ; } const { headers, bodyForm, pathParam, bodyOther, caseEnv, domains, method, pathname, query, bodyType } = this.state; const urlObj = URL.parse(domains.find(item => item.name === caseEnv).domain); let path = pathname pathParam.forEach(item => { path = path.replace(`:${item.name}`, item.value || `:${item.name}`); }); const href = URL.format({ protocol: urlObj.protocol || 'http', host: urlObj.host, pathname: path, query: this.getQueryObj(query) }); this.setState({ loading: true }) window.crossRequest({ url: href, method, headers: this.getHeadersObj(headers), data: bodyType === 'form' ? this.arrToObj(bodyForm) : bodyOther, files: bodyType === 'form' ? this.getFiles(bodyForm) : {}, success: (res, header) => { try { if (header && header['content-type'] && header['content-type'].indexOf('application/json') !== -1) { res = typeof res === 'object' ? res : JSON.parse(res) } if (header) { header = typeof header === 'object' ? header : JSON.parse(header) } } catch (e) { message.error(e.message) } const { res_body, res_body_type } = this.props.data; let validRes = ''; let query = {}; this.state.query.forEach(item=>{ query[item.name] = item.value; }) let body = {}; if(this.state.bodyType === 'form'){ this.state.bodyForm.forEach(item=>{ body[item.name] = item.value; }) }else if(this.state.bodyType === 'json'){ try{ body = JSON.parse(this.state.bodyOther); }catch(e){ body = {} } } if (res_body && res_body_type === 'json' && typeof res === 'object') { let tpl = MockExtra(JSON.parse(res_body), { query: query, body: body }) console.log(tpl, this.state) validRes = Mock.valid(tpl, res) console.log(validRes) } message.success('请求完成') this.setState({res, resHeader: header, validRes}) this.setState({ loading: false }) this.bindAceEditor() }, error: (err, header) => { try { if (header && header['content-type'] && header['content-type'].indexOf('application/json') !== -1) { err = typeof err === 'object' ? err : JSON.parse(err) } if (header) { header = typeof header === 'object' ? header : JSON.parse(header) } } catch (e) { message.error(e.message) } message.success('请求完成') this.setState({res: err || '请求失败', resHeader: header, validRes: null}) this.setState({ loading: false }) this.bindAceEditor() } }) } // @autobind // changeDomain(value) { // this.setState({ currDomain: value }); // } @autobind selectDomain(value) { this.setState({ caseEnv: value }); } @autobind changeHeader(e, index, isName) { const headers = JSON.parse(JSON.stringify(this.state.headers)); const v = e.target.value; if (isName) { headers[index].name = v; } else { headers[index].value = v; } this.setState({ headers }); } @autobind 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 setContentType(type) { const headersObj = this.getHeadersObj(this.state.headers); headersObj['Content-Type'] = type; this.setState({headers: this.objToArr(headersObj)}) } @autobind changeQuery(e, index, isKey) { const query = JSON.parse(JSON.stringify(this.state.query)); const v = e.target.value; if (isKey) { query[index].name = v; } else { query[index].value = v; } this.setState({ query }); } @autobind addQuery() { const { query } = this.state; this.setState({query: query.concat([{name: '', value: ''}])}) } @autobind deleteQuery(index) { const { query } = this.state; this.setState({query: query.filter((item, i) => +index !== +i)}); } @autobind changePathParam(e, index, isKey) { const pathParam = JSON.parse(JSON.stringify(this.state.pathParam)); const v = e.target.value; const name = pathParam[index].name; let newPathname = this.state.pathname; if (isKey) { if (!name && v) { newPathname += `/:${v}`; } else { newPathname = newPathname.replace(`/:${name}`, v ? `/:${v}` : '') } pathParam[index].name = v; } else { pathParam[index].value = v; } this.setState({ pathParam, pathname: newPathname }); } @autobind addPathParam() { const { pathParam } = this.state; this.setState({pathParam: pathParam.concat([{name: '', value: ''}])}) } @autobind deletePathParam(index) { const { pathParam } = this.state; const name = pathParam[index].name; const newPathname = this.state.pathname.replace(`/:${name}`, ''); this.setState({pathParam: pathParam.filter((item, i) => +index !== +i), pathname: newPathname}); } @autobind changeBody(e, index, type) { const bodyForm = JSON.parse(JSON.stringify(this.state.bodyForm)); switch (type) { case 'key': bodyForm[index].name = e.target.value break; case 'type': bodyForm[index].type = e break; case 'value': if (bodyForm[index].type === 'file') { bodyForm[index].value = e.target.id } else { bodyForm[index].value = e.target.value } break; default: break; } if (type === 'type' && e === 'file') { this.setContentType('multipart/form-data') } this.setState({ bodyForm }); } @autobind addBody() { const { bodyForm } = this.state; this.setState({bodyForm: bodyForm.concat([{name: '', value: '', type: 'text'}])}) } @autobind deleteBody(index) { const { bodyForm } = this.state; this.setState({bodyForm: bodyForm.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: this.objToArr(urlObj.query), pathname: urlObj.pathname }) } @autobind changeBodyType(value) { this.setState({bodyType: value}, () => { if(value !== 'file' && value !== 'form') { this.loadBodyEditor() } }) } // hasCrossRequestPlugin() { // const dom = document.getElementById('y-request'); // return dom.getAttribute('key') === 'yapi'; // } objToArr(obj, key, value) { const keyName = key || 'name'; const valueName = value || 'value'; const arr = [] Object.keys(obj).forEach((_key) => { if (_key) { arr.push({[keyName]: _key, [valueName]: obj[_key]}); } }) return arr; } arrToObj(arr) { const obj = {}; arr.forEach(item => { if (item.name && item.type !== 'file') { obj[item.name] = item.value || ''; } }) return obj; } getFiles(bodyForm) { const files = {}; bodyForm.forEach(item => { if (item.name && item.type === 'file') { files[item.name] = item.value } }) return files; } getQueryObj(query) { const queryObj = {}; query.forEach(item => { if (item.name) { queryObj[item.name] = item.value || ''; } }) return queryObj; } getHeadersObj(headers) { const headersObj = {}; headers.forEach(item => { if (item.name && item.value) { headersObj[item.name] = item.value; } }) return headersObj; } bindAceEditor = () => { mockEditor({ container: 'res-body-pretty', data: JSON.stringify(this.state.res, null, 2), readOnly:true, onChange: function () {} }) mockEditor({ container: 'res-headers-pretty', data: JSON.stringify(this.state.resHeader, null, 2), readOnly:true, onChange: function () {} }) } loadBodyEditor = () => { const that = this; setTimeout(function() { mockEditor({ container: 'body-other-edit', data: that.state.bodyOther, onChange: function (d) { if (d.format !== true) return false; that.setState({ bodyOther: d.text }) } }) }, 0); } @autobind fileChange(e, index) { console.log(e) console.log(index) } render () { const { method, domains, pathParam, pathname, query, headers, bodyForm, caseEnv, bodyType, resHeader, loading, validRes } = this.state; const hasPlugin = this.state.hasPlugin; let isResJson = false; Object.keys(resHeader).map(key => { if (/content-type/i.test(key) && /application\/json/i.test(resHeader[key])) { isResJson = true; } }) let path = pathname; pathParam.forEach(item => { path = path.replace(`:${item.name}`, item.value || `:${item.name}`); }); const search = decodeURIComponent(URL.format({query: this.getQueryObj(query)})); return (
{ hasPlugin ? '' : 温馨提示:当前正在使用接口测试服务,请安装我们为您免费提供的测试增强插件 (该插件可支持任何 chrome 内核的浏览器)
[Google 商店获取(需翻墙)]
[手动下载] zip 文件解压后将 crx 文件拖入到 chrome://extensions/ [详细安装教程]
} type="warning" /> }
{ pathParam.map((item, index) => { return (
this.changePathParam(e, index, true)} className="key" /> = this.changePathParam(e, index)} className="value" /> this.deletePathParam(index)} />
) }) }
{ query.map((item, index) => { return (
this.changeQuery(e, index, true)} className="key" /> = this.changeQuery(e, index)} className="value" /> this.deleteQuery(index)} />
) }) }
{ headers.map((item, index) => { return (
this.changeHeader(e, index, true)} className="key" /> = this.changeHeader(e, index)} className="value" /> this.deleteHeader(index)} />
) }) }
BODY
e.stopPropagation()} style={{marginRight: 5}}>
} key="3" className={method === 'POST' ? '' : 'hidden'} > { method === 'POST' && bodyType !== 'form' && bodyType !== 'file' &&
this.changeBodyType(e.target.value)}> JSON TEXT XML
} { method === 'POST' && bodyType === 'form' &&
{ bodyForm.map((item, index) => { return (
this.changeBody(e, index, 'key')} className="key" /> [ ] = { item.type === 'file' ? this.changeBody(e, index, 'value')} multiple className="value" /> : this.changeBody(e, index, 'value')} className="value" /> } this.deleteBody(index)} />
) }) }
} { method === 'POST' && bodyType === 'file' &&
} {/* method !== 'POST' &&
GET 请求没有 BODY。
*/}
返回 Body 验证结果:
{/**/}
) } }