使用Flask快速構建REST應用

引言

最近在某個小型項目上進行了一些嘗試喇辽,目標是用極快的速度構建一套簡潔且優(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應用。

  1. Web框架使用Flask
  2. ORM框架使用peewee
  3. 安全方面使用了flask-jwt插件進行JWT(Json Web Token)認證
  4. 由于flask-rest/flask-restplus侵略性較強鉴扫,本次沒有使用赞枕。
  5. 可以考慮自行使用flasgger添加SwaggerUI文檔。

第三方依賴

  • peewee
  • pymysql
  • Flask
  • Flask-JWT
  • flask-script

主要代碼示例

  1. 建模時盡量避免使用外鍵等約束條件,保證模型結構上一致炕婶,可以做到共用一個公共服務姐赡。
  2. 可使用@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)參數配置

  1. 編輯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'
  1. 編輯log-app.conf涯贞,修改日志路徑
args=('/path/to/log/flask-rest-sample.log','a','utf8')

數據庫初始化

  1. 自動建表
    直接運行python3 models.py

  2. 插入管理員用戶(默認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認證簡單說明

  1. 向/api/auth發(fā)起用戶認證請求
POST http://127.0.0.1:5000/api/auth
Content-Type: application/json
{
    "username": "admin",
    "password": "admin"
}
  1. 獲取響應,取得access_token
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0OTE2MzUyNTQsIm5iZiI6MTQ5MTYzNTI1NCwiaWRlbnRpdHkiOjEsImV4cCI6MTQ5MTYzNTU1NH0.wq-uer9LbRP5hEYGT4WfD5O4jf7k7du2Q1K6musKzvU"
}
  1. 在接下來的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"
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市傻谁,隨后出現的幾起案子,更是在濱河造成了極大的恐慌列粪,老刑警劉巖审磁,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異岂座,居然都是意外死亡态蒂,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門费什,熙熙樓的掌柜王于貴愁眉苦臉地迎上來钾恢,“玉大人,你說我怎么就攤上這事鸳址〈耱剑” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵稿黍,是天一觀的道長疹瘦。 經常有香客問我,道長巡球,這世上最難降的妖魔是什么言沐? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮酣栈,結果婚禮上险胰,老公的妹妹穿的比我還像新娘。我一直安慰自己矿筝,他們只是感情好起便,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般缨睡。 火紅的嫁衣襯著肌膚如雪鸟悴。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天奖年,我揣著相機與錄音细诸,去河邊找鬼。 笑死陋守,一個胖子當著我的面吹牛震贵,可吹牛的內容都是我干的。 我是一名探鬼主播水评,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼猩系,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了中燥?” 一聲冷哼從身側響起寇甸,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎疗涉,沒想到半個月后拿霉,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡咱扣,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年绽淘,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片闹伪。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡沪铭,死狀恐怖,靈堂內的尸體忽然破棺而出偏瓤,到底是詐尸還是另有隱情杀怠,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布厅克,位于F島的核電站驮肉,受9級特大地震影響,放射性物質發(fā)生泄漏已骇。R本人自食惡果不足惜离钝,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望褪储。 院中可真熱鬧卵渴,春花似錦、人聲如沸鲤竹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至碘橘,卻和暖如春互订,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背痘拆。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工仰禽, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人纺蛆。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓吐葵,卻偏偏與公主長得像,于是被迫代替她去往敵國和親桥氏。 傳聞我的和親對象是個殘疾皇子温峭,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內容