项目背景:
目前测试接口有些是依赖第三方接口,若第三方接口出现异常,会对测试进度有所影响。需要开发mock相关功能辅助测试。
技术选型:
1.前端:python+xadmin+django+mysql,通过界面录入生成wiremock下mapping、_files文件夹下的json文件;
2.后端:WireMock的standalone模式,通过shell脚本进行一键启停管理,以及实时刷新url、mapping映射。-----未完成
wiremock工作原理:
官网地址:http://wiremock.org/
最新jar包下载地址:http://repo1.maven.org/maven2/com/github/tomakehurst/wiremock-standalone/2.9.0/wiremock-standalone-2.9.0.jar
启动jar包:java -jar wiremock-standalone-2.9.0.jar –port 9999 —verbose
(–port设定端口为9999; –verbose开启日志。更多参数需要参考:
http://wiremock.org/docs/running-standalone/
启动后在同目录下生成两个空的文件夹:__files和mappings。__files是放上传/下载/录制文件的,mappings放response和request url的映射的。
在mappings文件夹下随便创建一个*.json文件,比如长下面这样:
"request": { "method": "GET", "url": "/api/testdetail" }, "response": { "status": 200, "bodyFileName": "testdetail.json”, "headers": { "Content-Type": "application/json", "Cache-Control": "max-age=86400" } } }
bodyFileName还可以是html、xml等文档。
在浏览器或者使用curl命令,调用http://localhost:9999/api/testdetail,就能返回testdetail.json的内容了。testdetail.json就是需要我们在__files里面建立的响应文件。wiremock也支持直接在response结构体中返回响应内容,比如在mapping文件中,将response写成下面这样:
"response": { "status": 200, "body": “Hello world ", "headers": { "Content-Type": "application/json", "Cache-Control": "max-age=86400" }
当发送请求时候,将直接返回“Hello world”。
前端实现:
1.思路分析:
1)添加:
支持get和post两种,其中:get可带请求参数,也可不带;post请求必带请求参数;返回类型可为text,也可为json类型;添加后应根据选择请求方式和请求返回类型生成不同格式的json文件;
2)删除:
删除时应同时删除已生成的json文件;
2.代码部分:
views.py:
from __future__ import unicode_literals from django.shortcuts import render,redirect,HttpResponse from mockserver import models from .mock_forms import * from django.db.models import Q from utils.pagination import * from mockjson import * import json # Create your views here. class JsonCustomEncoder(json.JSONEncoder): def default(self, field): if isinstance(field, ValidationError): return {'code': field.code, 'messages': field.messages} else: return json.JSONEncoder.default(self, field) def MockManage(request,mid): '''mock接口管理''' if request.method == "GET": method_type = request.GET.get("method_type") keywords = request.GET.get("keywords") reset = request.GET.get("reset") if reset: return redirect('/mock/mockmanage-%s' % mid) else: project_info = models.MockProject.objects.filter(id=mid).values("mockname", 'id') if not method_type: method_type = 0 elif int(method_type) == 1: selectstatus = "GET" elif int(method_type) == 2: selectstatus = "POST" if not keywords: keywords = "" if int(method_type) == 0: selectstatus = u"全部" counts = models.MockDetail.objects.filter(Q(mock_name__contains=keywords),De_Pro_id = mid).count() if counts: mockinfo = models.MockDetail.objects.filter(Q(mock_name__contains=keywords),De_Pro_id = mid).values( 'id', "mock_name", "mock_summary", "mock_method", "mock_url", "mock_status", "mock_query" ).order_by("id") current_page = request.GET.get('p', 1) current_page = int(current_page) page_obj = Page(current_page, counts) try: mockinfo = mockinfo[page_obj.start:page_obj.end] now_url = request.get_full_path() if "&p=" in now_url: now_url = (now_url.split("&p="))[0] elif "?p=" in now_url: now_url = (now_url.split("?p="))[0] page_str = page_obj.page_str(now_url) except Exception: mockinfo = 0 page_str = 0 return render( request, 'mockcenter/mockmanage.html',{ 'project_info':project_info, 'counts':counts, "mockinfo":mockinfo, 'keywords': keywords, 'selectstatus': selectstatus, "page_str": page_str, } ) else: counts = 0 return render( request, 'mockcenter/mockmanage.html', { 'project_info': project_info, 'counts': counts, 'keywords': keywords, 'selectstatus': selectstatus, } ) elif int(method_type) == 1 or int(method_type) == 2: counts = models.MockDetail.objects.filter(Q(mock_name__contains=keywords), De_Pro_id=mid,mock_method=selectstatus).count() if counts: mockinfo = models.MockDetail.objects.filter(Q(mock_name__contains=keywords), mock_method=selectstatus,De_Pro_id=mid).values( 'id', "mock_name", "mock_summary", "mock_method", "mock_url", "mock_status", "mock_query" ).order_by("id") current_page = request.GET.get('p', 1) current_page = int(current_page) page_obj = Page(current_page,counts) try: mockinfo = mockinfo[page_obj.start:page_obj.end] now_url = request.get_full_path() if "&p=" in now_url: now_url = (now_url.split("&p="))[0] elif "?p=" in now_url: now_url = (now_url.split("?p="))[0] page_str = page_obj.page_str(now_url) except Exception: mockinfo = 0 page_str = 0 return render( request, 'mockcenter/mockmanage.html', { 'project_info': project_info, 'counts': counts, "mockinfo": mockinfo, 'keywords': keywords, 'selectstatus': selectstatus, "page_str": page_str, } ) else: counts = 0 return render( request, 'mockcenter/mockmanage.html', { 'project_info': project_info, 'counts': counts, 'keywords': keywords, 'selectstatus': selectstatus, } ) def AddMock(request,mid): '''增加mock接口''' if request.method == "GET": project_info = models.MockProject.objects.filter(id=mid).values("mockname", 'id','mockdetail__mock_method') return render( request, 'mockcenter/mockadd.html',{ 'mid':mid, 'project_info': project_info, } ) elif request.method == "POST": mockname = request.POST.get("mockname") mocksummary = request.POST.get("mocksummary") mock_method = request.POST.get("mock_method") mockurl = request.POST.get("mockurl") mockquery = request.POST.get("mockquery") mockstatus = request.POST.get("mockstatus") response_json = request.POST.get("response_json") response_text = request.POST.get("response_text") mockheaders = request.POST.get("mockheaders") mockseconds = request.POST.get("mockseconds") mock_stype = request.POST.get("mock_stype") ret={'status':False,'data':None,'error':None} obj = Mock_FM(request.POST) if obj.is_valid(): if str(mock_stype) == "TEXT": mockresponse = response_text print(mockresponse) elif str(mock_stype) == "JSON": mockresponse = response_json mock_info = { "mockname":mockname, "url":mockurl, "method":mock_method, "mockquery":mockquery, "status":mockstatus, "mock_type":mock_stype, "response":mockresponse, "headers":mockheaders, "mockseconds":mockseconds } Make_mockjson(mock_info) #往wiremock的mappings和__files文件中新增json文件 models.MockDetail.objects.create( De_Pro_id=mid, mock_name=mockname, mock_method=mock_method, mock_url=mockurl, mock_summary=mocksummary, mock_status=mockstatus, mock_stype = mock_stype, mock_reponse=mockresponse, mock_query=mockquery, reponse_headers=mockheaders, mock_seconds=mockseconds ) ret['status'] = True else: print(obj.errors.as_data()) ret['status'] = False ret['error'] = obj.errors.as_data() result = json.dumps(ret,cls=JsonCustomEncoder) return HttpResponse(result) def DelMock(request,mid): '''删除mock接口''' row_id = request.POST.get("row_id") if row_id: row_id = row_id.split(",") if len(row_id) > 1: for i in row_id[1:]: models.MockDetail.objects.filter(id=i,De_Pro_id=mid).delete() else: models.MockDetail.objects.filter(id=row_id[0], De_Pro_id=mid).delete() return redirect("/mock/mockmanage-%s"%mid) def EditMock(request,mid,cid): if request.method == "GET": project_info = models.MockProject.objects.filter(id=mid).values("mockname",'id') mockinfo = models.MockDetail.objects.filter(De_Pro_id=mid,id=cid).all() return render( request, "mockcenter/mockedit.html",{ "mid":mid, "project_info":project_info, "mockinfo":mockinfo }) elif request.method == "POST": mockname = request.POST.get("mockname") mocksummary = request.POST.get("mocksummary") mock_method = request.POST.get("mock_method") mockurl = request.POST.get("mockurl") mockquery = request.POST.get("mockquery") mockstatus = request.POST.get("mockstatus") mockresponse = request.POST.get("mockresponse") mockheaders = request.POST.get("mockheaders") mockseconds = request.POST.get("mockseconds") ret = {'status': False, 'data': None, 'error': None} obj = Mock_FM(request.POST) if obj.is_valid(): old_mockinfo = models.MockDetail.objects.filter(De_Pro_id=mid,mock_name=mockname).values("id") try: if int(old_mockinfo[0]["id"]) != int(cid): edict_dir = {} edict_dir['mockname']= [ValidationError(u"已存在相同名称的接口,请重新输入!")] ret['status'] = False ret['error'] = edict_dir else: models.MockDetail.objects.filter(De_Pro_id=mid, id=cid).update( mock_name=mockname, mock_method=mock_method, mock_url=mockurl, mock_summary=mocksummary, mock_status=mockstatus, mock_reponse=mockresponse, mock_query=mockquery, reponse_headers=mockheaders, mock_seconds=mockseconds ) ret['status'] = True except Exception: models.MockDetail.objects.filter(De_Pro_id=mid, id=cid).update( mock_name=mockname, mock_method=mock_method, mock_url=mockurl, mock_summary=mocksummary, mock_status=mockstatus, mock_reponse=mockresponse, mock_query=mockquery, reponse_headers=mockheaders, mock_seconds=mockseconds ) ret['status'] = True else: ret['status'] = False print(obj.errors.as_data()) ret['error'] = obj.errors.as_data() result = json.dumps(ret, cls=JsonCustomEncoder) return HttpResponse(result)
mock_forms.py:
# -*- coding:utf-8 -*- # __author__ == 'cc' from django import forms from django.forms import fields from django.core.exceptions import ValidationError from mockserver import models import re,json class Mock_FM(forms.Form): mockname = fields.CharField( max_length=64, required=True, error_messages={ 'required':u'接口名称不能为空', 'max_length':u"接口名称最多为64个字符", } ) mocksummary = fields.CharField( max_length=512, required=True, error_messages={ 'required': u'接口描述不能为空', 'max_length': u"接口描述最多为512个字符", } ) mock_method = fields.CharField( required=True ) mock_stype = fields.CharField( required=True ) mockurl = fields.CharField( max_length=255, required=True, error_messages={ 'required': u'url不能为空', 'max_length': u"url最多为255个字符", } ) mockquery = forms.CharField( max_length=512, required=False, error_messages={ 'max_length': u"请求参数最多为512个字符", } ) mockstatus = forms.IntegerField(error_messages={ 'required':u"状态码不能为空", "invalid":u"必须输入正确的状态码", }) response_text = fields.CharField( max_length=6144, required=False, error_messages={ 'max_length': u"返回结果最多为6144个字符", } ) response_json = fields.CharField( max_length=6144, required=False, error_messages={ 'max_length': u"返回结果最多为6144个字符", } ) mockheaders = fields.CharField( max_length=2048, required=False, error_messages={ 'max_length': u"返回头最多为2048个字符", } ) mockseconds = forms.CharField( max_length=255, required=False, error_messages={ 'max_length': u"延迟时间最多为255个字符", } ) def clean_mock_name(self): mockname = self.cleaned_data['mock_name'] if mockname: c = models.MockDetail.objects.filter(mock_name=mockname).count() if c: raise ValidationError(u"接口描述已存在,请重新输入!") else: return mockname def clean_mock_url(self): mock_url = self.cleaned_data['mock_url'] if mock_url: c = models.MockDetail.objects.filter(mock_url=mock_url).count() if c: raise ValidationError(u"接口描述已存在,请重新输入!") else: return mock_url def clean_mockquery(self): mockquery = self.cleaned_data['mockquery'] mock_method = self.cleaned_data['mock_method'] if mock_method == "GET": if mockquery: mockquery = str(mockquery).replace("'", '"') try: mockquery = json.loads(mockquery) if isinstance(mockquery, dict): return mockquery else: raise ValidationError(u"请输入正确格式的请求参数!") except ValueError: raise ValidationError(u"请输入正确格式的请求参数!") else: mockquery = "" return mockquery elif mock_method == "POST": if mockquery: mockquery = str(mockquery).replace("'", '"') try: mockquery = json.loads(mockquery) if isinstance(mockquery, dict): return mockquery else: raise ValidationError(u"请输入正确格式的请求参数!") except ValueError: raise ValidationError(u"请输入正确格式的请求参数!") else: raise ValidationError(u"请输入正确格式的请求参数!") def clean_mockresponse(self): mock_stype = self.cleaned_data['mock_stype'] if str(mock_stype) == "TEXT": mockresponse = self.cleaned_data['response_text'] if mockresponse: mockresponse = str(mockresponse).replace("'", '"') return mockresponse else: raise ValidationError(u"返回结果不能为空!") elif str(mock_stype) == "JSON": mockresponse = self.cleaned_data['response_json'] if mockresponse: mockresponse = str(mockresponse).replace("'", '"') try: mockresponse = json.loads(mockresponse) if isinstance(mockresponse, dict): return mockresponse else: raise ValidationError(u"请输入正确格式的返回结果!") except ValueError: raise ValidationError(u"请输入正确格式的返回结果!") else: raise ValidationError(u"返回结果不能为空!") def clean_mockheaders(self): mockheaders = self.cleaned_data['mockheaders'] mock_stype = self.cleaned_data['mock_stype'] mockheaders = str(mockheaders).replace("'", '"') if str(mock_stype) == "TEXT": return mockheaders elif str(mock_stype) == "JSON": try: mockheaders = json.loads(mockheaders) if isinstance(mockheaders,dict): return mockheaders else: raise ValidationError(u"请输入正确格式的返回头!") except ValueError: raise ValidationError(u"请输入正确格式的返回结果头!") def clean_mockseconds(self): mockseconds = self.cleaned_data['mockseconds'] if mockseconds: return mockseconds else: mockseconds = "" return mockseconds
生成json文件:
# -*- coding:utf-8 -*- # __author__ == 'cc' import json,os def Make_mockjson(mockinfo): '''获取mock信息,创建符合wiremock要求的json文件''' json_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) mapping_name = "%s_%s%s.json" % (mockinfo['url'].split("/")[-1],mockinfo['method'],mockinfo['status']) #mapping夹下的json文件名:截取url最后路径_接口方式_状态码 file_name = "file_%s%s.json"%(mockinfo['url'].split("/")[-1],mockinfo['status'])#__file文件夹下的json文件名:file_截取url最后路径_状态码 if mockinfo['method'] == "GET": if mockinfo['mockquery']: mockquery = json.loads(mockinfo['mockquery']) query_infos = "" for k,v in enumerate(mockquery): query_infos +="%s=%s&"%(v,mockquery[v]) #参数组合成k1=v1&k2=v2格式 if str(mockinfo['mock_type']) == "JSON": #接口方式为get、有请求参数且返回类型为json时,创建如下格式的json文件 json_data = { "request":{ "method":"GET", "urlPattern":r"%s?%s"%(mockinfo['url'],query_infos[:-1]), }, "response":{ "status":mockinfo['status'], "bodyFileName":"%s.json"%file_name, "headers":json.loads(mockinfo['headers']) } } elif str(mockinfo['mock_type']) == "TEXT":#接口方式为get、有请求参数且返回类型为text时,创建如下格式的json文件 json_data = { "request": { "method": "GET", "urlPattern": r"%s?%s" % (mockinfo['url'], query_infos[:-1]), }, "response": { "status": mockinfo['status'], "body": mockinfo['response'], "headers": json.loads(mockinfo['headers']) } } else: if str(mockinfo['mock_type']) == "JSON": #接口方式为get,m json_data = { "request":{ "method":"GET", "url":mockinfo['url'] }, "response":{ "status":mockinfo['status'], "bodyFileName": "%s.json" % file_name, "headers": json.loads(mockinfo['headers']) } } elif str(mockinfo['mock_type']) == "TEXT": json_data = { "request": { "method": "GET", "url": mockinfo['url'] }, "response": { "status": mockinfo['status'], "body": mockinfo['response'], } } elif mockinfo['method'] == "POST": mockquery = json.loads(mockinfo['mockquery']) json_query = json.dumps(mockquery) json_query_new=json_query.replace('"',r'"') if str(mockinfo['mock_type']) == "JSON": json_data = { "request": { "method": "POST", "url": mockinfo['url'], "bodyPatterns":[ { "equalToJson":"%s"%json_query_new, "jsonCompareMode":"LENIENT" } ] }, "response": { "status": mockinfo['status'], "bodyFileName": "%s" % file_name, "headers": json.loads(mockinfo['headers']) } } elif str(mockinfo['mock_type']) == "TEXT": json_data = { "request": { "method": "POST", "url": mockinfo['url'], "bodyPatterns": [ { "equalToJson": "%s" % json_query_new, "jsonCompareMode": "LENIENT" } ] }, "response": { "status": mockinfo['status'], "body": mockinfo['response'], } } mapping_data = json.dumps(json_data) mapping_path = os.path.join(json_dir,'wiremock/mappings/%s'%mapping_name) with open(mapping_path,'wb+') as f: f.write(mapping_data) if str(mockinfo['mock_type']) == "JSON": file_path =os.path.join(json_dir,'wiremock\__files\%s'%file_name) post_response = json.dumps(str(mockinfo['response'])) with open(file_path,'wb+') as f1: f1.write(json.loads(post_response)) def Del_mockjson(): '''删除json文件''' pass
html模板:
1)mockmanage.html
<div class="navbar content-navbar navbar-default navbar-xs" data-toggle="breakpoint" data-class-xs="navbar content-navbar navbar-inverse navbar-xs" data-class-sm="navbar content-navbar navbar-default navbar-xs"> <div class="navbar-header"> <button class="navbar-toggle pull-left" data-toggle="class" data-target="#body-content" data-class-name="show_menu"> <i class="fa fa-list"></i> </button> <a class="navbar-toggle pull-right"><i class="fa fa-plus"></i></a> <button class="navbar-toggle pull-right" data-toggle="collapse" data-target=".content-navbar .navbar-collapse"> <i class="fa fa-filter"></i> </button> <a class="navbar-brand" data-toggle="collapse" data-target="#top-nav .navbar-collapse"> <i class="fa fa-eercast"></i>[{{ project_info.0.mockname }}] MOCK信息 </a> </div> <div class="navbar-collapse collapse"> {% if user.is_superuser %} <div class="navbar-btn pull-left hide-xs" id="Del_chos"> {% csrf_token %} <a class="btn btn_del"><i class="fa fa-minus"></i> 批量删除 </a> </div> {% endif %} <div class="mockadd_reset"> <div class="navbar-btn pull-right hide-xs"> <a href="../mockmanage-{{ project_info.0.id }}/add" class="btn btn-primary"><i class="fa fa-plus"></i> 增加 Mock接口 </a> </div> </div> </div> </div> <div class="classdiv"> <div class="selecth4"> <span>接口查询</span> </div> <form id="search_mock" method="get" style="display: inline-block"> <div class="selecttitle"> <span> <i class="fa fa-hand-o-right selectsize">接口名称:</i> <input type="text" class="input-title" placeholder="请输入要查询的接口名称" name="keywords" value="{{ keywords }}"/> </span> </div> <div class="selectstatus"> <span> <i class="fa fa-hand-o-right selectsize">接口类型:</i> <select class="status-select" name="method_type"> {% if selectstatus == "GET" %} <option value="0">全部</option> <option value="1" selected="selected">GET</option> <option value="2">POST</option> {% elif selectstatus == "POST" %} <option value="0">全部</option> <option value="1">GET</option> <option value="2" selected="selected">POST</option> {% else %} <option value="0" selected="selected">全部</option> <option value="1">GET</option> <option value="2">POST</option> {% endif %} </select> </span> </div> <button class="btn btn-primary selectbtn" type="submit">查询接口</button> <input class="selreset btn btn-primary" type="submit" name="reset" value="重置查询"/> </form> </div> <ul class="pagination pagination-sm pagination-left pagination-inline"> <li><span><span class="text-success">{{ counts }}</span> 接口信息</span></li> </ul> <div class="results table-responsive"> {% if counts %} <table class="table table-bordered table-striped table-hover"> <thead> <tr> <th scope="col" class="action-checkbox-column"> <input type="checkbox" class="action-all"/> </th> <th scope="col" class="th_title"> 接口名称 </th> <th scope="col" class="th_title"> 接口描述 </th> <th scope="col" class="th_title"> 接口类型 </th> <th scope="col" class="th_title"> URL </th> <th scope="col" class="th_title"> 状态码 </th> <th scope="col" class="th_title"> 操作 </th> </tr> </thead> <tbody> {% for m in mockinfo %} <tr class="grid-item"> <td class="relatd"> <input type="checkbox" value="{{ m.id }}" name="ck" class="c_checkbox"/> </td> <td class="relatd mname"> <span>{{ m.mock_name }}</span> </td> <td class="relatd mname"> <span>{{ m.mock_summary }}</span> </td> <td class="relatd cstatus"> <span>{{ m.mock_method }}</span> </td> <td class="relatd urlcs"> <span>{{ m.mock_url }}</span> </td> <td class="relatd cstatus"> <span>{{ m.mock_status }}</span> </td> <td class="relatd"> <div style="position: absolute;top:2%"> <a class="fa fa-align-center case_action_edit" href="edit-{{ m.id }}"> <span>编辑</span> </a> {% if user.is_superuser %} <a class="fa fa-minus-square case_action_del" row_id = {{ m.id }}> <span> 删除</span> </a> {% endif %} </div> </td> </tr> {% endfor %} </tbody> </table> {% else %} <div style="text-align: center"> <span>暂无Mock接口信息</span> </div> {% endif %} <div class="v-transfer-dom hide" id="Del_dom"> <div class="ivu-modal-mask"></div> <div class="ivu-modal-wrap"> <div class="ivu-modal" style=" 416px;"> <div class="ivu-modal-content"> <div class="ivu-modal-body"> <div class="ivu-modal-confirm"> <div class="ivu-modal-confirm-head"> <div class="ivu-modal-confirm-head-title">提示</div> </div> <div class="ivu-modal-confirm-body"> <div class="ivu-modal-confirm-body-icon ivu-modal-confirm-body-icon-confirm"> <i class="fa fa-question"></i> </div> <div>该操作无法撤消,是否继续删除?</div> </div> <form class="ivu-modal-confirm-footer" method="post" action="delmock"> {% csrf_token %} <input type="hidden" id="row_id" name="row_id"/> <button type="button" class="ivu-btn ivu-btn-text ivu-btn-large" id="del_console"> <span>取消</span> </button> <button type="submit" class="ivu-btn ivu-btn-primary ivu-btn-large"> <span>确定</span> </button> </form> </div> </div> </div> </div> </div> </div> <div class="clearfix pagesty"> <div class="pagination right" style="margin-top: 0"> {{ page_str }} </div> </div> <a href="#" class="fixedtool">顶部</a> </div>
2)mockadd.html
<div class="navbar content-navbar navbar-default navbar-xs" data-toggle="breakpoint" data-class-xs="navbar content-navbar navbar-inverse navbar-xs" data-class-sm="navbar content-navbar navbar-default navbar-xs"> <div class="navbar-header"> <button type="button" class="navbar-toggle pull-left" onclick="javascript: history.back();"><i class="fa fa-arrow-left"></i></button> <a class="navbar-brand" data-toggle="collapse" data-target="#top-nav .navbar-collapse"> <i class="fa fa-eercast"><sub class="fa fa-plus"></sub></i> 增加 [{{ project_info.0.mockname }}]接口信息 </a> </div> <div class="navbar-collapse collapse"> <ul class="nav navbar-nav"> </ul> <div class="navbar-btn pull-right hide-xs"> </div> </div> </div> <form class="exform rended" enctype="multipart/form-data" method="post" id="mockadd_form"> <input type="hidden" name="csrfmiddlewaretoken" value="{{ csrf_token }}"/> <input type="hidden" id="mockproject_id" value="{{ mid }}"/> <div class="form-body"> <div id="div_mockname" class="casegroup"> <label class="group-label"> 接口名称 <span style="color:red">*</span> </label> <div class="control_text"> <input class="case_input" type="text" name="mockname" id="text" placeholder="请输入接口名称"/> <span id="mockname_error" class="error_cs"></span> </div> </div> <div id="div_mocksummary" class="casegroup"> <label class="group-label"> 接口描述 <span style="color:red">*</span> </label> <div class="control_text"> <input class="case_input" type="text" name="mocksummary" id="text" placeholder="请输入接口描述"/> <span id="summary_error" class="error_cs"></span> </div> </div> <div id="div_mock_method" class="casegroup"> <label class="group-label"> 方法类型 <span style="color:red">*</span> </label> <div class="control_text"> <select class="status-select" name="mock_method"> <option value="GET">GET</option> <option value="POST">POST</option> </select> </div> </div> <div id="div_mockurl" class="casegroup"> <label class="group-label"> URL <span style="color:red">*</span> </label> <div class="control_text"> <input class="case_input" type="text" name="mockurl" id="id_mockurl" placeholder="请输入URL,格式:/%s/%s"/> <span id="url_error" class="error_cs"></span> </div> </div> <div id="div_casestep" class="case_textarea"> <label class="group-label"> 请求参数 </label> <div class="control_text"> <textarea class="case_input textarea_nor" type="text" name="mockquery" placeholder='请输入json类型参数,如{"key1":"val1","key2":"val2"}' id="id_casestep"></textarea> <span id="query_error" class="error_cs"></span> </div> </div> <div id="div_mockstatus" class="casegroup"> <label class="group-label"> 返回状态码 <span style="color:red">*</span> </label> <div class="control_text"> <input class="case_input" type="text" name="mockstatus" id="text" placeholder="请输入返回状态码"/> <span id="status_error" class="error_cs"></span> </div> </div> <div id="div_mock_rtype" class="casegroup"> <label class="group-label"> 返回类型 <span style="color:red">*</span> </label> <div class="control_text"> <select class="status-select" name="mock_stype" id="mock_stype" onchange="ValChange()"> <option value="TEXT">TEXT</option> <option value="JSON">JSON</option> </select> </div> </div> <div id="return_json" class="hide"> <div class="case_textarea"> <label class="group-label"> 返回结果 <span style="color:red">*</span> </label> <div class="control_text"> <textarea class="case_input textarea_nor" type="text" placeholder='请输入json类型返回结果,如{"key1":"val1","key2":"val2"}' name="response_json"></textarea> <span id="resopnse_error" class="error_cs"></span> </div> </div> <div id="div_headers" class="case_textarea"> <label class="group-label"> 返回头 <span style="color:red">*</span> </label> <div class="control_text"> <textarea class="case_input textarea_nor" type="text" placeholder='请输入json类型返回头,如{"key1":"val1","key2":"val2"}' name="mockheaders" id="id_mockheaders"></textarea> <span id="headers_error" class="error_cs"></span> </div> </div> </div> <div class="case_textarea" id="return_text"> <label class="group-label"> 返回结果 <span style="color:red">*</span> </label> <div class="control_text"> <textarea class="case_input textarea_nor" type="text" placeholder='请输入text类型返回结果' name="response_text"></textarea> <span id="resopnse_error" class="error_cs"></span> </div> </div> <div id="div_mockseconds" class="casegroup"> <label class="group-label"> 响应延迟 </label> <div class="control_text"> <input class="case_input" type="text" name="mockseconds" id="text" placeholder="请输入响应延迟时间,单位:毫秒"/> <span id="seconds_error" class="error_cs"></span> </div> </div> </div> <div class="form-actions well well-sm clearfix"> <a id="addmocknow" class="default btn btn-primary hide-xs"> <i class="fa fa-save"></i> 保存 </a> <div class="nav-collapse collapse more-btns"> <a id="mockanother" class="btn btn-default">保存并增加另一个</a> </div> </div> </form>
3)mockedit.html
<div class="navbar content-navbar navbar-default navbar-xs" data-toggle="breakpoint" data-class-xs="navbar content-navbar navbar-inverse navbar-xs" data-class-sm="navbar content-navbar navbar-default navbar-xs"> <div class="navbar-header"> <button type="button" class="navbar-toggle pull-left" onclick="javascript: history.back();"><i class="fa fa-arrow-left"></i></button> <a class="navbar-brand" data-toggle="collapse" data-target="#top-nav .navbar-collapse"> <i class="fa fa-eercast"><sub class="fa fa-plus"></sub></i> 编辑 [{{ project_info.0.mockname }}]接口信息 </a> </div> <div class="navbar-collapse collapse"> <ul class="nav navbar-nav"> </ul> <div class="navbar-btn pull-right hide-xs"> </div> </div> </div> <form class="exform rended" enctype="multipart/form-data" method="post" id="mockedit_form"> <input type="hidden" name="csrfmiddlewaretoken" value="{{ csrf_token }}"/> <input type="hidden" id="mockproject_id" value="{{ mid }}"/> {% for m in mockinfo %} <input type="hidden" id="edit_id" value="{{ m.id }}"/> <div class="form-body"> <div id="div_mockname" class="casegroup"> <label class="group-label"> 接口名称 <span style="color:red">*</span> </label> <div class="control_text"> <input class="case_input" type="text" name="mockname" id="text" value="{{ m.mock_name }}"/> <span id="mockname_error" class="error_cs"></span> </div> </div> <div id="div_mocksummary" class="casegroup"> <label class="group-label"> 接口描述 <span style="color:red">*</span> </label> <div class="control_text"> <input class="case_input" type="text" name="mocksummary" id="text" value="{{ m.mock_summary }}"/> <span id="summary_error" class="error_cs"></span> </div> </div> <div id="div_mock_method" class="casegroup"> <label class="group-label"> 方法类型 <span style="color:red">*</span> </label> <div class="control_text"> <select class="status-select" name="mock_method"> {% if m.mock_method == "GET" %} <option value="GET" selected="selected">GET</option> <option value="POST">POST</option> {% elif m.mock_method == "POST" %} <option value="GET">GET</option> <option value="POST" selected="selected">POST</option> {% endif %} </select> </div> </div> <div id="div_mockurl" class="casegroup"> <label class="group-label"> URL <span style="color:red">*</span> </label> <div class="control_text"> <input class="case_input" type="text" name="mockurl" id="id_mockurl" value="{{ m.mock_url }}"/> <span id="url_error" class="error_cs"></span> </div> </div> <div id="div_casestep" class="case_textarea"> <label class="group-label"> 请求参数 </label> <div class="control_text"> <textarea class="case_input textarea_nor" type="text" name="mockquery" id="id_casestep">{{ m.mock_query }}</textarea> <span id="query_error" class="error_cs"></span> </div> </div> <div id="div_mockstatus" class="casegroup"> <label class="group-label"> 返回状态码 <span style="color:red">*</span> </label> <div class="control_text"> <input class="case_input" type="text" name="mockstatus" id="text" value="{{ m.mock_status }}"/> <span id="status_error" class="error_cs"></span> </div> </div> <div id="div_response" class="case_textarea"> <label class="group-label"> 返回结果 <span style="color:red">*</span> </label> <div class="control_text"> <textarea class="case_input textarea_nor" type="text" name="mockresponse" id="id_mockresponse">{{ m.mock_reponse }}</textarea> <span id="resopnse_error" class="error_cs"></span> </div> </div> <div id="div_headers" class="case_textarea"> <label class="group-label"> 返回头 <span style="color:red">*</span> </label> <div class="control_text"> <textarea class="case_input textarea_nor" type="text" name="mockheaders" id="id_mockheaders">{{ m.reponse_headers }}</textarea> <span id="headers_error" class="error_cs"></span> </div> </div> <div id="div_mockseconds" class="casegroup"> <label class="group-label"> 响应延迟 </label> <div class="control_text"> <input class="case_input" type="text" name="mockseconds" id="text" placeholder="请输入响应延迟时间,单位:毫秒" value="{{ m.mock_seconds }}"/> <span id="seconds_error" class="error_cs"></span> </div> </div> </div> {% endfor %} <div class="form-actions well well-sm clearfix"> <a id="editmocknow" class="default btn btn-primary hide-xs"> <i class="fa fa-save"></i> 保存 </a> <div class="nav-collapse collapse more-btns"> <a id="console_Editmock" class="btn btn-default" href="./">取消</a> </div> </div> </form>
4)mock.js
function ValChange() { var val = $("#mock_rtype").val(); if(val == "TEXT"){ $("#return_json").addClass("hide"); $("#return_text").removeClass("hide"); }else{ $("#return_json").removeClass("hide"); $("#return_text").addClass("hide"); } }; $.ajaxSetup({ beforeSend:function (xhr,settings){ xhr.setRequestHeader('X-CSRFtoken',$.cookie('csrftoken')); } }); $("#addmocknow").click(function () { var mid=$("#mockproject_id").val(); $.ajax({ url: "/mock/mockmanage-%s/add"%mid, type: "POST", data:$('#mockadd_form').serialize(), success: function (data) { var obj = JSON.parse(data); if (obj.status) { location.href="./";//若点击保存按钮,保存后返回到上级页面 }else{ if (obj.error.mockname){ $('#mockname_error').text(obj.error.mockname[0].messages); }else{ if(obj.error.mocksummary){ $("#summary_error").text(obj.error.mocksummary[0].messages) }else { if (obj.error.mockurl) { $("#url_error").text(obj.error.mockurl[0].messages) } else { if (obj.error.mockquery){ $("#query_error").text(obj.error.mockquery[0].messages) }else{ if (obj.error.mockstatus) { $("#status_error").text(obj.error.mockstatus[0].messages) }else { if (obj.error.mockresponse) { $("#resopnse_error").text(obj.error.mockresponse[0].messages) } else { if (obj.error.mockheaders) { $("#headers_error").text(obj.error.mockheaders[0].messages) }else{ $("#headers_error").text(obj.error) } } } } } } } } } }) }); $("#addanother").click(function () { var mid=$("#modeltype").val(); $.ajax({ url: "/case/casemanage-%s/add"%mid, type: "POST", data:$('#caseadd_form').serialize(), success: function (data) { var obj = JSON.parse(data); if (obj.status) { location.reload();//若点击保存并增加另一个则刷新当前页面 }else{ $('#error_msg').text(obj.error); } } }) }); $(".case_action_del").each(function () { $(this).click(function () { $(this).parents().find(".del_text").removeClass("hide"); var row_id = $(this).attr("row_id"); $("#row_id").val(row_id); }) }); $("#Del_chos").click(function () { $("#Del_dom").removeClass("hide"); }) $("#del_console").click(function () { $("#Del_dom").addClass("hide"); }); $("#editmocknow").click(function () { var mid=$("#mockproject_id").val(); var cid = $("#edit_id").val(); $.ajax({ url: "/mock/mockmanage-%s/edit-%s"%(mid,cid), type: "POST", data:$('#mockedit_form').serialize(), success: function (data) { var obj = JSON.parse(data); if (obj.status) { location.href="./";//若点击保存按钮,保存后返回到上级页面 }else{ if (obj.error.mockname){ $('#mockname_error').text(obj.error.mockname[0].messages); }else{ if(obj.error.mocksummary){ $("#summary_error").text(obj.error.mocksummary[0].messages) }else { if (obj.error.mockurl) { $("#url_error").text(obj.error.mockurl[0].messages) } else { if (obj.error.mockquery){ $("#query_error").text(obj.error.mockquery[0].messages) }else{ if (obj.error.mockstatus) { $("#status_error").text(obj.error.mockstatus[0].messages) }else { if (obj.error.mockresponse) { $("#resopnse_error").text(obj.error.mockresponse[0].messages) } else { if (obj.error.mockheaders) { $("#headers_error").text(obj.error.mockheaders[0].messages) }else{ $("#headers_error").text(obj.error) } } } } } } } } } }) });
后端部分
未实现---待补
最终效果: