Python(七) —— mock接口開發

mock接口開發

接口開發有不少框架,諸如 Django,flask,相比較而言,flask 是輕量級web開發框架,用來開發 mock 接口的不二之選。那你可能會問,什麼叫 mock 接口呢?mock 的意思是模擬。html

mock 接口的使用場景

場景1html5

假定在作接口測試,你正在編寫自動化腳本,可是依賴於另外一個接口的返回數據,可是另外一個接口有問題/未開發完成,那麼就須要構造接口的數據。這時候,咱們能夠利用 mock 接口的方式,構造出一個接口來造出咱們須要的返回數據。從而不由於其餘模塊而阻礙當前進度。python

場景2mysql

假設公司內部部門不一樣,部門之間有交互,交互的話,假設別人想要個人訂單表數據,可是我不想把數據庫暴露給別人,那能夠開發一個 mock 接口,這樣他們能夠經過這個接口訪問數據庫,可是殊不知道數據庫的進入方式以及數據庫形式。git

接下來咱們就利用 flask 來構造 mock 接口,其中包括 get 請求,post 的 key-value 形式,json 形式,上傳文件,訪問數據庫等web

模板

首先安裝好 flask 模塊:pip install Flask ,其次開始寫接口,接口是有模板的,具體以下:redis

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

咱們對其進行解讀:sql

  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請求

一、獲取請求參數數據庫

 其實上面的接口就是個 get 接口的形式,可是是相對而言比較簡單的,那麼咱們的接口當中,get 請求有的也是要加參數的,好比說 ?username=xxxpasswd=xxx 那麼怎麼辦呢?json

@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 形式的

一、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 形式內的參數值

二、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)

 三、上傳文件

@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 代碼等等,這樣就是一個小的網頁咯

相關文章
相關標籤/搜索