接口開發有不少框架,諸如 Django,flask,相比較而言,flask 是輕量級web開發框架,用來開發 mock 接口的不二之選。那你可能會問,什麼叫 mock 接口呢?mock 的意思是模擬。html
場景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
{ code: 0, msg: "ganziwen登錄成功", session_id: "sdf234sdsdfsdfs" }
一、獲取請求參數數據庫
其實上面的接口就是個 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
和 get 形式的有些許不一樣,在 meythods 內指定 = ['post'],默認是 get 形式的
@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 形式內的參數值
@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)
配置文件
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' }
接口:
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)
接口實現:
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
文件說明:
#這個程序是寫xxx接口的 入口文件是main.py config下面是配置文件 lib是程序的主邏輯在這裏面 須要安裝的第三方模塊 flask pymysql
引用模塊
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
這裏還有個問題:安裝以及引用的第三方模塊須要本身寫嗎?不須要!咱們本機全部的模塊其實能夠用命令導出,換了電腦也能夠用命令批量安裝:這個命令要在命令行作
pip freeze > 第三方模塊.txt # 將安裝過的模塊導出到指定文件 pip install -r 第三方模塊.txt # 將文件內的模塊批量安裝, -r 表明遍歷
其實呢,能作的僅僅如此麼?咱們以前是以 json 串爲主,可是其實也能夠寫字符串,html 代碼等等,這樣就是一個小的網頁咯