zoukankan      html  css  js  c++  java
  • Python(七) —— mock接口开发

    mock接口开发

    接口开发有很多框架,诸如 Django,flask,相比较而言,flask 是轻量级web开发框架,用来开发 mock 接口的不二之选。那你可能会问,什么叫 mock 接口呢?mock 的意思是模拟。

    mock 接口的使用场景

    场景1

    假定在做接口测试,你正在编写自动化脚本,但是依赖于另一个接口的返回数据,但是另一个接口有问题/未开发完成,那么就需要构造接口的数据。这时候,我们可以利用 mock 接口的方式,构造出一个接口来造出我们需要的返回数据。从而不因为其他模块而阻碍当前进度。

    场景2

    假设公司内部部门不同,部门之间有交互,交互的话,假设别人想要我的订单表数据,但是我不想把数据库暴露给别人,那可以开发一个 mock 接口,这样他们可以通过这个接口访问数据库,但是却不知道数据库的进入方式以及数据库形式。

    接下来我们就利用 flask 来构造 mock 接口,其中包括 get 请求,post 的 key-value 形式,json 形式,上传文件,访问数据库等

    模板

    首先安装好 flask 模块:pip install Flask ,其次开始写接口,接口是有模板的,具体如下:

    import flask,json
    
    server = flask.Flask(__name__)
    
    @server.route('/login')
    def welcome():
        data = {'code':0,'msg':'ganziwen登陆成功','session_id':'sdf234sdsdfsdfs'}
        return json.dumps(data,ensure_ascii=False)
    
    server.run(host='0.0.0.0',port=8888,debug=True) #5000

    我们对其进行解读:

    1. server=flask.Flask(__name__)  # 这个可以理解为固定写法,意思是起一个 flask 的服务
    2. @server.route('/login')  # 这是个装饰器,代表下面的函数不是个普通的函数,而是一个接口,/login 是接口路径,跟下面的函数名不一定要一致,假设设置为 /,那么就是代表该接口下的默认接口
    3. def welcome():  # 这个是接口内的函数,要跟上一行紧挨着,且函数名不能有重复的
    4. return 是返回什么玩意,这里我们返回 json 串。假设要更好看点,可以在后面加:indent = 4,这样格式化起来更好看
    5. server.run(host='0.0.0.0',port=8888,debug=True)  # 这个是代表启动接口的服务,host 可以为 127.0.0.1,这样只能本机访问,如果想要局域网内的用户都能访问,那么设置成 0.0.0.0 ,port 代表启动服务的端口,默认是 5000 ,debug=True 代表自动调试,假设改了接口代码,不用重新运行,以免忘记去重新运行启动服务
    6. 运行该服务,编译期内看到 * Running on http://0.0.0.0:8888/ (Press CTRL+C to quit) ;且在浏览器内访问 127.0.0.1:8888/login 查看到是我们定义的结果如下
    {
    code: 0,
    msg: "ganziwen登陆成功",
    session_id: "sdf234sdsdfsdfs"
    }

    get请求

    1、获取请求参数

     其实上面的接口就是个 get 接口的形式,但是是相对而言比较简单的,那么我们的接口当中,get 请求有的也是要加参数的,比如说 ?username=xxxpasswd=xxx 那么怎么办呢?

    @server.route('/urldata') #get请求,参数在url里面的
    def urlData():
        u = flask.request.args.get('username')
        p = flask.request.args.get('password')
        data  = {'username':u,'password':p}
        return json.dumps(data,ensure_ascii=False)

    flask.request.args.get('key')  # 这个就是接口内需要传的参数 key,前面用变量获取

    这时候,访问对应的地址就应该加上这两个参数,比如:http://127.0.0.1:8888/urldata?username=123&password=456 

    结果

    {
    username: "123",
    password: "456"
    }

    可以明显的看到,传的参数,显示在结果内,我们的函数要实现的功能也是如此

    假设要传的参数未传,那么参数获取到的值就是 None,反映在结果内就是 Null

    post请求

    和 get 形式的有些许不同,在 meythods 内指定 = ['post'],默认是 get 形式的

    1、form-data 形式

    @server.route('/add_student',methods=['post'])
    def add_stu():
    
        stu_name = flask.request.values.get('name')
        pwd = flask.request.values.get('pwd')
    
        return json.dumps({'msg':'添加成功!'},ensure_ascii=False)

    flask.request.values.get('key')  # 获取 form-data 形式内的参数值

    2、json形式

    @server.route('/add_student2',methods=['post'])
    def add_stu2():
        if flask.request.is_json:  # 判断 request 的形式是否为 json 形式,如果不加这个判断,传进来为 key-value 形式就会报错 AttributeError
            stu_name = flask.request.json.get('name')
            pwd = flask.request.json.get('pwd')
    #print(flask.request.json)  # 可以打印出传进来的所有参数 
    return json.dumps({'msg':'添加成功!'},ensure_ascii=False) else: return json.dumps({'msg':'入参请传入json'},ensure_ascii=False)

     3、上传文件

    @server.route('/file',methods=['post'])
    def uploadFile():
        file = flask.request.files.get('f')
        print(file.filename) #获取到上传的文件名
        #path ='~/Desktop/'+file.filename    #保存文件的路径,这个可以写成绝对路径
    #file.save(path)  # 和上面一行是成对出现,将文件保存到绝对路径 file.save(file.filename) #保存,这样是保存到 python 文件的目录下 # print(dir(file)) return json.dumps({'msg':'上传完成!'},ensure_ascii=False)

    这个其实没有规避掉一个问题,文件名重复的怎么办?可以在文件名后面加一个时间戳,这样上传就基本不会有一样的了,这里我们就不写太多

    从数据库获取数据

    比如说,其他部门想要获取某个库,但是不想把整个数据库暴露给别人,怎么办呢?可以用接口实现,比如说:传一个表就给你返回这个表的数据:/table_data?table_name=xxx&limit=xxx

    首先要写好 sql 的执行函数,之前我们写过:

    def op_mysql(sql:str):
        mysql_info = {
            'host': '118.24.3.40',
            'port': 3306,
            'password': '123456',
            'user': 'jxz',
            'db': 'jxz',
            'charset': 'utf8',
            'autocommit': True
        }
        result = '执行完成'
        conn = pymysql.connect(**mysql_info)
        cur = conn.cursor(pymysql.cursors.DictCursor) #建立游标
        cur.execute(sql)
        if sql.strip().lower().startswith('select'):
            # result  = cur.fetchone()
            result  = cur.fetchall()
        cur.close()
        conn.close()
        return result

    接口内容:

    import flask,json
    server = flask.Flask(__name__)
    @server.route('/table_data')
    def get_table_data():
        table = ['app_myuser','dsk_test','app_student']
        table_name = flask.request.args.get('table_name')
        limit = flask.request.args.get('limit','10')  #10 为默认值
        if not table_name:
            return json.dumps({'msg':'table_name是必填字段'},ensure_ascii=False)
        if table_name not in table:
            return json.dumps({'msg':'没权限访问'},ensure_ascii=False)
        if limit.isdigit():
            sql = 'select * from %s limit %s;'%(table_name,limit)
        else:
             return json.dumps({'msg':'limit 请传入整数'},ensure_ascii=False)
        res = op_mysql(sql)
        return json.dumps(res,ensure_ascii=False)
    
    server.run(host='0.0.0.0',port = 5000,debug=True)

    这个里面是写好了刚刚的要求,设定的表是可以定义的,不在里面的表访问不了,报没权限;

    limit 判断了是否为整数,默认值为 10 ,也可以自己改;

    table_name 必须传,没传就会报其是一个必填字段

    这个是 get 实现,也可以自己定义为 post 实现

    结果如下:

    [
    {
    id: 422,
    username: "glw",
    passwd: "123455",
    is_admin: 123,
    error_count: 0
    },
    {
    id: 424,
    username: "glw1",
    passwd: "123455",
    is_admin: 123,
    error_count: 0
    },
    ……
    ]

    程序分目录

    在正常的开发中,不可能像我们之前写的那样,什么都扔在一个文件里面,逻辑,数据,配置全部写在一起的话,难以维护。那么有没有什么框架来维护呢?接下来看一下,一个简单的框架是怎样的

    API
        |__bin    # 执行文件的目录
        |   |__start.py
        |
        |__config    # 配置文件,数据库,服务器端口,md5 加盐值等
        |   |__setting.py
        |
        |__lib    # 实现函数
        |   |__interface.py    # 接口实现
        |   |__tools.py    # 小工具:操作 mysql ,加盐等
        |
        |__logs    # 日志文件夹
        |
        |__readme.txt    # 文件以及文件夹内部内容说明
        |
        |__第三方模块.txt    # 整个工程安装运用的第三方模块说明

    配置文件路径

    在程序分目录的过程中,涉及到一个问题:模块之间的引用,在 windows 的 pycharm 内,可以很快地设置环境变量:在程序的主目录上,例如上述就是在 API 文件夹==>右键==>Mark Directory as ==> Source Root,设置好后,API 文件夹为蓝色,那么这个文件夹下的模块就可以相互引用。而且存在一个问题,假设 API 的同一级目录有一个:API2 也设置了 Source Root ,这样 API2 导入模块也没用,所以说 Source Root 只能有一个。

    或者在启动文件内,将文件的路径写进去,这样也可以

    那么,你可能会问,那我要把程序放到 Linux 内,没有 pycharm 怎么用?有没有终极解决方案呢?有的!

    我们在 bin 的 start.py 里面可以设置,将 API 的路径加入到 python 环境变量内:

    import sys
    import os
    BASE_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    sys.path.insert(0,BASE_PATH)

    os.path.abspath(__file__)  代表本文件路径

    os.path.dirname()  代表文件的父目录

    也就是这里取了两级父目录,取几级视情况而定

    实例

    为了更好的理解目录分级,我们这里举个例子说明一下目录分级的文件:

    启动文件:

    import sys
    import os
    BASE_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    sys.path.insert(0,BASE_PATH)
    
    from config.setting import server_info
    from lib.interface import server
    
    server.run(**server_info)
    start.py

    配置文件

    mysql_info = {
        'host': '118.24.3.40', #ip
        'port': 3306, #端口号
        'password': '123456', #密码
        'user': 'jxz', #用户
        'db': 'jxz',#数据库
        'charset': 'utf8',
        'autocommit': True
    }
    
    redis_info = {
        'host': '118.24.3.40', #ip
        'port': 6379, #端口号
        'password': 'HK139bc&*', #密码,
        'db':0
    }
    
    SALT = '@#@$#%SD324532sfd' #
    
    
    
    server_info  = {
        'port':8888,#端口号
        'debug':True,#是否调试模式
        'host':'0.0.0.0'
    }
    setting.py

    接口:

    import flask,json
    from . import tools
    
    server = flask.Flask(__name__)
    
    @server.route('/table_data')
    def get_table_data():
        #没有实现校验表是否存在
        tables = ['app_myuser','dsk_test','app_student','app_product']
        table_name = flask.request.args.get('table_name')
        limit = flask.request.args.get('limit','10')
        if table_name not in tables:
            return json.dumps({'msg':'没有获取这个表的权限!'},ensure_ascii=False)
        if not table_name:
            return json.dumps({'msg':"table_name是必填字段"},ensure_ascii=False)
        if limit.isdigit():
            sql = 'select * from %s limit %s; '%(table_name,limit)
        else:
            return json.dumps({'msg':'limit请传入一个整数!'},ensure_ascii=False)
        result = tools.op_mysql(sql)
        return json.dumps(result,ensure_ascii=False)
    
    @server.route('/add_mem',methods=['post'])
    def add_mem():
        #没有实现校验表是否存在
        #qq_mem
        username = flask.request.json.get('username')
        password = flask.request.json.get('password')
        if username and password :
            sql='select * from app_myuser where username="%s";'%username
            if tools.op_mysql(sql):
                data = {'msg':'用户已经存在'}
            else:
                new_password = tools.md5(password)
                insert_sql = 'insert into app_myuser (username,passwd) value ("%s","%s");'%(username,new_password)
                data = {'msg':'用户添加成功!'}
                tools.op_mysql(insert_sql)
        else:
            data = {'msg':'必填参数未填,请查看接口文档!'}
        return json.dumps(data,ensure_ascii=False)
    interface.py

    接口实现:

    import hashlib,pymysql
    from config import setting
    
    def md5(s,):
        s = (str(s)+setting.SALT).encode()
        m = hashlib.md5(s)#加密
        return m.hexdigest()
    
    
    def op_mysql(sql:str):
        result = '执行完成'
        conn = pymysql.connect(**setting.mysql_info)
        cur = conn.cursor(pymysql.cursors.DictCursor) #建立游标
        cur.execute(sql)
        if sql.strip().lower().startswith('select'):
            # result  = cur.fetchone()
            result  = cur.fetchall()
        cur.close()
        conn.close()
        return result
    tools.py

    文件说明:

    #这个程序是写xxx接口的
    
    入口文件是main.py
    
    config下面是配置文件
    
    lib是程序的主逻辑在这里面
    
    
    需要安装的第三方模块
    flask
    pymysql
    readme.txt

    引用模块

    alabaster==0.7.10
    anaconda-client==1.6.14
    anaconda-navigator==1.8.7
    anaconda-project==0.8.2
    appnope==0.1.0
    appscript==1.0.1
    asn1crypto==0.24.0
    astroid==1.6.3
    astropy==3.0.2
    attrs==18.1.0
    Babel==2.5.3
    backcall==0.1.0
    backports.shutil-get-terminal-size==1.0.0
    beautifulsoup4==4.6.0
    bitarray==0.8.1
    bkcharts==0.2
    blaze==0.11.3
    bleach==2.1.3
    bokeh==0.12.16
    boto==2.48.0
    Bottleneck==1.2.1
    certifi==2018.4.16
    cffi==1.11.5
    chardet==3.0.4
    click==6.7
    cloudpickle==0.5.3
    clyent==1.2.2
    colorama==0.3.9
    conda==4.6.14
    conda-build==3.10.5
    conda-verify==2.0.0
    contextlib2==0.5.5
    cryptography==2.2.2
    cycler==0.10.0
    Cython==0.28.2
    cytoolz==0.9.0.1
    dask==0.17.5
    datashape==0.5.4
    decorator==4.3.0
    distributed==1.21.8
    Django==2.1.7
    docutils==0.14
    entrypoints==0.2.3
    et-xmlfile==1.0.1
    Faker==1.0.2
    fastcache==1.0.2
    filelock==3.0.4
    Flask==1.0.2
    Flask-Cors==3.0.4
    gevent==1.3.0
    glob2==0.6
    gmpy2==2.0.8
    greenlet==0.4.13
    h5py==2.7.1
    heapdict==1.0.0
    html5lib==1.0.1
    idna==2.6
    imageio==2.3.0
    imagesize==1.0.0
    ipykernel==4.8.2
    ipython==6.4.0
    ipython-genutils==0.2.0
    ipywidgets==7.2.1
    isort==4.3.4
    itsdangerous==0.24
    jdcal==1.4
    jedi==0.12.0
    Jinja2==2.10
    jsonpath==0.80
    jsonschema==2.6.0
    jupyter==1.0.0
    jupyter-client==5.2.3
    jupyter-console==5.2.0
    jupyter-core==4.4.0
    jupyterlab==0.32.1
    jupyterlab-launcher==0.10.5
    kiwisolver==1.0.1
    lazy-object-proxy==1.3.1
    llvmlite==0.23.1
    locket==0.2.0
    lxml==4.2.1
    MarkupSafe==1.0
    matplotlib==2.2.2
    mccabe==0.6.1
    mistune==0.8.3
    mkl-fft==1.0.0
    mkl-random==1.0.1
    more-itertools==4.1.0
    mpmath==1.0.0
    msgpack-python==0.5.6
    multipledispatch==0.5.0
    navigator-updater==0.2.1
    nbconvert==5.3.1
    nbformat==4.4.0
    networkx==2.1
    nltk==3.3
    nnlog==1.0.4
    nose==1.3.7
    nose-parameterized==0.6.0
    notebook==5.5.0
    numba==0.38.0
    numexpr==2.6.5
    numpy==1.14.3
    numpydoc==0.8.0
    odo==0.5.1
    olefile==0.45.1
    openpyxl==2.5.3
    packaging==17.1
    pandas==0.23.0
    pandocfilters==1.4.2
    parso==0.2.0
    partd==0.3.8
    path.py==11.0.1
    pathlib2==2.3.2
    patsy==0.5.0
    pbr==4.2.0
    pep8==1.7.1
    pexpect==4.5.0
    pickleshare==0.7.4
    Pillow==5.1.0
    pkginfo==1.4.2
    pluggy==0.6.0
    ply==3.11
    prompt-toolkit==1.0.15
    psutil==5.4.5
    ptyprocess==0.5.2
    py==1.5.3
    pycodestyle==2.4.0
    pycosat==0.6.3
    pycparser==2.18
    pycrypto==2.6.1
    pycurl==7.43.0.1
    pyflakes==1.6.0
    Pygments==2.2.0
    pylint==1.8.4
    PyMySQL==0.9.2
    pyodbc==4.0.23
    pyOpenSSL==18.0.0
    pyparsing==2.2.0
    PySocks==1.6.8
    pytest-arraydiff==0.2
    pytest-astropy==0.3.0
    pytest-doctestplus==0.1.3
    pytest-openfiles==0.3.0
    pytest-remotedata==0.2.1
    python-dateutil==2.7.3
    pytz==2018.4
    PyWavelets==0.5.2
    PyYAML==3.12
    pyzmq==17.0.0
    QtAwesome==0.4.4
    qtconsole==4.3.1
    QtPy==1.4.1
    redis==3.0.1
    requests==2.18.4
    rope==0.10.7
    ruamel-yaml==0.15.35
    scikit-image==0.13.1
    scikit-learn==0.19.1
    scipy==1.1.0
    seaborn==0.8.1
    Send2Trash==1.5.0
    simplegeneric==0.8.1
    singledispatch==3.4.0.3
    six==1.11.0
    snowballstemmer==1.2.1
    sortedcollections==0.6.1
    sortedcontainers==1.5.10
    Sphinx==1.7.4
    sphinxcontrib-websupport==1.0.1
    spyder==3.2.8
    SQLAlchemy==1.2.7
    statsmodels==0.9.0
    stevedore==1.29.0
    sympy==1.1.1
    tables==3.4.3
    tblib==1.3.2
    terminado==0.8.1
    testpath==0.3.1
    text-unidecode==1.2
    toolz==0.9.0
    tornado==5.0.2
    traitlets==4.3.2
    typing==3.6.4
    unicodecsv==0.14.1
    urllib3==1.22
    virtualenv==16.0.0
    virtualenv-clone==0.3.0
    virtualenvwrapper==4.8.2
    wcwidth==0.1.7
    webencodings==0.5.1
    Werkzeug==0.14.1
    widgetsnbextension==3.2.1
    wrapt==1.10.11
    xlrd==1.1.0
    XlsxWriter==1.0.4
    xlutils==2.0.0
    xlwings==0.11.8
    xlwt==1.2.0
    xpinyin==0.5.6
    yagmail==0.10.212
    zict==0.1.3
    第三方模块.txt

    这里还有个问题:安装以及引用的第三方模块需要自己写吗?不需要!我们本机所有的模块其实可以用命令导出,换了电脑也可以用命令批量安装:这个命令要在命令行做

    pip freeze > 第三方模块.txt    # 将安装过的模块导出到指定文件
    pip install -r 第三方模块.txt    # 将文件内的模块批量安装, -r 代表遍历

    注意

    1. 重启服务的时候,切记不要把程序再运行一次,那样是重新启动了一个服务而不是重新启动之前的服务,正确做法是 Rerun 一次已经运行的服务
    2. 有时候点右键运行如果出现,那么运行会报错,正常应该是,这个是 pycharm 的问题,只要出现'xxx in xxx.py',解决办法是点击最上面的 Run,选择 Run...,再选择要运行的代码
    3. 只写 ip:port 不成,还等加上相应路径,否则会报 404 
    4. 路径不能有重复的,函数名也不能有重复的
    5. flask.request 就是 flask 收到的所有参数,打印的类型可以再后面加比如:flask.request.json
    6. server.run()  只能写一个,而且只能写在最后,在这之后的代码运行不到

    衍生

    其实呢,能做的仅仅如此么?我们之前是以 json 串为主,但是其实也可以写字符串,html 代码等等,这样就是一个小的网页咯

  • 相关阅读:
    Seaborn相关
    Matplot相关(二)——统计图
    PAT 甲级真题
    数学题一
    Codeforces Round #467 (Div. 2)
    国庆 Day1
    [NOIP 2005] 运输计划
    dp专题练习
    YBT 2.4 AC自动机
    [模板]树链剖分
  • 原文地址:https://www.cnblogs.com/xiaowenshu/p/10888263.html
Copyright © 2011-2022 走看看