平台支持mock功能—未完成版

项目背景:

目前测试接口有些是依赖第三方接口,若第三方接口出现异常,会对测试进度有所影响。需要开发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)
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        })
    });

后端部分

未实现---待补

最终效果:

原文地址:https://www.cnblogs.com/cocc/p/12365701.html