引言
最近在某個小型項目上進行了一些嘗試喇辽,目標是用極快的速度構建一套簡潔且優(yōu)雅的小型WEB系統(tǒng)蜘犁。當然這次嘗試可以說是失敗的扔字,稍后會提到喂急。
架構上主要是前后端分離格嘁,后端使用Python實現RESTFul API,前端直接套用AdminLTE模版廊移。
得益于Python動態(tài)語言的特性糕簿,后端可以做到非常少量的代碼實現增刪改查等基本功能探入,大約是可以使用100多行代碼寫了25個REST服務吧,非常驚人。雖然比較簡陋跟衅,但比起之前使用Spring Boot全家桶來開發(fā)REST闷畸,要快得太多。
但比起后端植旧,前端的代碼量,初版寫出來之后离唐,是后端的幾十倍病附,使用gulp構建,include等插件后亥鬓,做到了一定簡化完沪,接下來還要做SPA化,引入vue.js嵌戈、webpack等等覆积,最終會變成 為了目的不擇手段,為了手段而忘記目的 熟呛。一個很重要的前提是宽档,要做一個 小型WEB系統(tǒng) ,整體成本要小惰拱。所以前后端分離雖然好處多多雌贱,但前端太重了,并不適合小型系統(tǒng)偿短。
最終還是使用了Flask+Jinja欣孤,傳統(tǒng)的重后端、輕前端的方式來實現昔逗,將總體代碼量控制到了極少降传。
但這次的REST API部分,不失為一次有益的嘗試勾怒,單獨拿出來做成腳手架婆排,提供給有需要的人吧。
源碼
http://git.si-tech.com.cn/guolei/flask-rest-sample
https://github.com/xiiiblue/flask-rest-sample
簡介
flask-rest-sample 是一個使用Python編寫的REST API笔链,可以對多個資源提供增刪改查服務段只,用于快速構建REST應用。
- Web框架使用Flask
- ORM框架使用peewee
- 安全方面使用了flask-jwt插件進行JWT(Json Web Token)認證
- 由于flask-rest/flask-restplus侵略性較強鉴扫,本次沒有使用赞枕。
- 可以考慮自行使用flasgger添加SwaggerUI文檔。
第三方依賴
- peewee
- pymysql
- Flask
- Flask-JWT
- flask-script
主要代碼示例
- 建模時盡量避免使用外鍵等約束條件,保證模型結構上一致炕婶,可以做到共用一個公共服務姐赡。
- 可使用
@rest.route
添加多個資源
# 基本API
@rest.route('/api/reports', methods=['GET', 'POST'])
@rest.route('/api/reports/<id>', methods=['GET', 'PUT', 'DELETE'])
@rest.route('/api/res2', methods=['GET', 'POST'])
@rest.route('/api/res2/<id>', methods=['GET', 'PUT', 'DELETE']) #只要模型結構一致,可以添加多個資源
@jwt_required()
def common_rest_api(id=None):
model_name = request.path.split('/')[2]
pee_wee_model = utils.get_model_by_name(model_name) #從URL中確定模型
if not pee_wee_model:
return utils.jsonresp(status=400, errinfo='model_name不正確')
if id:
# 查詢
if request.method == 'GET':
try:
model = pee_wee_model.get(pee_wee_model.id == id)
except:
return utils.jsonresp(status=404, errinfo='查詢不到資料')
return utils.jsonresp(jsonobj=utils.obj_to_dict(model))
# 修改
elif request.method == 'PUT':
json_dict = request.get_json(silent=True, force=True)
if not json_dict: return utils.jsonresp(status=400, errinfo='參數格式不正確')
try:
model = pee_wee_model.get(pee_wee_model.id == id)
except:
return utils.jsonresp(status=404, errinfo='查詢不到資料')
utils.dict_to_obj(dict=json_dict, obj=model, exclude=('id',)) # 去掉ID字段
model.save()
return utils.jsonresp(status=201)
# 刪除
elif request.method == 'DELETE':
try:
pee_wee_model.get(pee_wee_model.id == id).delete_instance()
except:
return utils.jsonresp(status=404, errinfo='查詢不到資料')
return utils.jsonresp(status=204)
else:
return utils.jsonresp(status=405, errinfo='不支持的HTTP方法')
else:
# 全量查詢(支持分頁柠掂、排序项滑、搜索)
if request.method == 'GET':
# 處理查詢參數
logger.debug(request.args)
try:
# 當前頁碼
page = request.args.get('page', '')
if page: page = int(page) + 1
# 每頁展示數量
length = request.args.get('length', '')
if length:
length = int(length)
else:
length = cfg.ITEMS_PER_PAGE
# 排序
sort = request.args.get('sort', '')
if sort:
sort_column = sort.split(',')[0]
sort_direction = sort.split(',')[1]
except:
return utils.jsonresp(status=400, errinfo='參數格式不正確')
# 查詢
query = pee_wee_model.select()
total_count = query.count()
# 排序
if sort:
if sort_column in pee_wee_model._meta.fields:
field = getattr(pee_wee_model, sort_column)
if sort_direction != 'asc':
field = field.desc()
query = query.order_by(field)
# 分頁
if page:
query = query.paginate(page, length)
dict = {'content': utils.query_to_list(query), 'totalElements': total_count}
return utils.jsonresp(jsonobj=dict)
# 新增
elif request.method == 'POST':
json_dict = request.get_json(silent=True, force=True)
if not json_dict: return utils.jsonresp(status=400, errinfo='參數格式不正確')
user = utils.dict_to_obj(dict=json_dict, obj=pee_wee_model(), exclude=['id']) # 去掉ID字段
user.save()
return utils.jsonresp(status=201)
else:
return utils.jsonresp(status=405, errinfo='不支持的HTTP方法')
環(huán)境配置
venv虛擬環(huán)境安裝配置
sudo pip3 install virtualenv
virtualenv venv
. venv/bin/activate
第三方依賴安裝
pip3 install -r requirements.txt
系統(tǒng)參數配置
- 編輯
config.py
, 修改SECRET_KEY及MySQL數據庫相關參數
SECRET_KEY = os.environ.get('SECRET_KEY') or 'your-secret'
DB_HOST = '127.0.0.1'
DB_USER = 'foobar'
DB_PASSWD = 'foobar'
DB_DATABASE = 'foobar'
- 編輯log-app.conf涯贞,修改日志路徑
args=('/path/to/log/flask-rest-sample.log','a','utf8')
數據庫初始化
自動建表
直接運行python3 models.py
插入管理員用戶(默認admin/admin)
INSERT INTO `user` (`id`, `username`, `password`, `fullname`, `email`, `phone`, `status`)
VALUES
(1, 'admin', 'pbkdf2:sha1:1000$Km1vdx3W$9aa07d3b79ab88aae53e45d26d0d4d4e097a6cd3', '管理員', 'admin@admin.com', '18612341234', 1);
啟動應用
nohup ./manage.py runserver 2>&1 &
或
./run_app_dev.py (僅限測試)
REST接口說明
以reports資源為例:
- GET /api/reports/<id> 查詢
200 成功
PUT /api/reports/<id> 修改
201 成功DELETE /api/reports/<id> 刪除
204 成功POST /api/reports 新增
200 成功GET /api/reports 全量查詢
200 成功
支持分頁(URL參數:page枪狂、length)及排序(URL參數:sort)
參數示例:?page=1&length=5&sort=serial_number,desc
JWT認證簡單說明
- 向/api/auth發(fā)起用戶認證請求
POST http://127.0.0.1:5000/api/auth
Content-Type: application/json
{
"username": "admin",
"password": "admin"
}
- 獲取響應,取得access_token
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0OTE2MzUyNTQsIm5iZiI6MTQ5MTYzNTI1NCwiaWRlbnRpdHkiOjEsImV4cCI6MTQ5MTYzNTU1NH0.wq-uer9LbRP5hEYGT4WfD5O4jf7k7du2Q1K6musKzvU"
}
- 在接下來的HTTP請求頭中宋渔,加入
Authorization
摘完,值為JWT+空格+access_token
以獲取當前用戶信息為例
請求:
GET http://127.0.0.1:5000/api/identity
Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0OTE2MzUyNTQsIm5iZiI6MTQ5MTYzNTI1NCwiaWRlbnRpdHkiOjEsImV4cCI6MTQ5MTYzNTU1NH0.wq-uer9LbRP5hEYGT4WfD5O4jf7k7du2Q1K6musKzvU
響應:
{
"email": "admin@admin.com",
"fullname": "管理員",
"username": "admin",
"phone": "18612341234"
}