flask-restful擴充套件
阿新 • • 發佈:2020-11-01
1、檢視
1.1、擴充套件介紹
Flask-RESTful 是一個 Flask 擴充套件,它添加了快速構建 REST APIs 的支援。
- 安裝flask-restful
pip install flask-restful
- 文件
1.2、構建RESTAPI
使用 flask-restful
構建RESTAPI只需要進行三步操作
- 建立擴充套件/元件物件
- 元件物件 = Api(app)
- 定義類檢視
- class 自定義檢視類(Resource)
- 元件新增類檢視
- 元件物件.add_resource(檢視類,url資源段)
程式碼示例:
from flask import Flask from flask_restful import Api, Resource app = Flask(__name__) # 1. 建立API物件 用於管理類檢視(資源) api = Api(app) # 2.定義類檢視 繼承Resource class DemoResource(Resource): def get(self): # 類檢視響應的content-type預設變為json形式 # 類檢視的返回值可以是字典, 會被自動轉為json字串View Codereturn {'foo': 'get'} def post(self): return {'foo': 'post'} # 3.新增類檢視 函式標記預設為類名小寫 api.add_resource(DemoResource, '/', endpoint='demo') # @app.route('/') # def index(): # # return "index" if __name__ == '__main__': print(app.url_map) app.run(debug=True)
類檢視的優點
- 類檢視響應的
content-type
預設變為 JSON形式 - 類檢視的 返回值可以是字典, 會被自動轉為json字串
1.3、類檢視設定裝飾器
- 通過
method_decorators
類屬性 來設定類檢視的裝飾器 - 該屬性接收兩種型別資料
- 列表形式: 所有請求方式都會使用列表中的裝飾器
- 字典形式: 給請求方式分別設定裝飾器
from flask import Flask from flask_restful import Resource, Api app = Flask(__name__) api = Api(app) def deco1(f): def wrapper(*args, **kwargs): print('deco1') return f(*args, **kwargs) return wrapper def deco2(f): def wrapper(*args, **kwargs): print('deco2') return f(*args, **kwargs) return wrapper class DemoResource(Resource): # 通過method_decorators類屬性來設定類檢視的裝飾器 # method_decorators = [deco1, deco2] # 列表形式 所有請求方式都會使用 method_decorators = {'get': [deco1], 'post': [deco2]} # 字典形式 給請求方式分別設定裝飾器 # @deco2 # @deco1 def get(self): return {'foo': "get"} def post(self): return {'foo': "post"} api.add_resource(DemoResource, '/') if __name__ == '__main__': app.run(debug=True)View Code
1.4、藍圖和類檢視
藍圖和類檢視可以配合使用, 步驟如下:
- 建立藍圖物件
- 藍圖物件 = Blueprint(“藍圖名”, __name__)
- 每個藍圖分別建立元件物件
- 元件物件 = Api(藍圖物件)
- 元件物件新增類檢視
- 元件物件.add_resource(檢視類,url資源段)
- 應用註冊藍圖
- 應用物件.register_blueprint(藍圖物件)
程式碼示例:
- 在
user
包中建立views
檔案, 定義類檢視
# user/views.py from flask_restful import Resource class DemoResource(Resource): def get(self): return {'get': 'hello'} def post(self): return {'post': 'world'}View Code
- 在
user
包 的初始化檔案__init__.py
中: - 建立藍圖物件
- 通過藍圖物件建立對應的元件物件
- 元件物件新增類檢視
# user/__init__.py from flask import Blueprint from flask_restful import Api from user.views import DemoResource # 1.建立藍圖物件 user_blu = Blueprint('user', __name__, url_prefix='/user') # 2.建立藍圖對應的api物件 user_api = Api(user_blu) # 3.新增類檢視 user_api.add_resource(DemoResource, '/')View Code
- 想要讓藍圖物件能夠完成路由定義, 還需要 Flask應用註冊藍圖物件
# main.py from flask import Flask from user import user_blu app = Flask(__name__) # 4.註冊藍圖 app.register_blueprint(user_blu) if __name__ == '__main__': print(app.url_map) app.run(debug=True, port=8000)View Code
2、請求
2.1、請求解析
- 通過前一階段的學習, 我們知道
DRF框架
的核心功能 序列化器, 包括以下功能:- 反序列化
- 引數解析
- 資料儲存
- 序列化
- 反序列化
flask-restful
也實現了類似的功能:- 引數解析
RequestParser
- 序列化
marshal函式
- 引數解析
flask-restful
沒有提供資料儲存功能, 資料儲存可以使用後續的flask-sqlalchemy
擴充套件 來完成
2.2、RequestParser
RequestParser
負責請求解析工作, 基本步驟如下:
- 建立請求解析器
請求解析器 = ResquestParser()
請求解析器.add_argument(引數名, 引數規則..)
引數物件 = 請求解析器.parse_args()
引數物件.引數名
程式碼示例:
from flask import Flask from flask_restful import Resource, Api from flask_restful.reqparse import RequestParser app = Flask(__name__) api = Api(app) class DemoResource(Resource): def get(self): # 1.建立請求解析器 parser = RequestParser() # 2.新增引數規則 parser.add_argument('name') parser.add_argument('age') # 3.執行解析 預設會從 查詢字串/post鍵值對/post-json資料 進行引數提取 args = parser.parse_args() # 4.獲取引數 print(args.name) print(args.age) return {'foo': "get"} api.add_resource(DemoResource, '/') if __name__ == '__main__': app.run(debug=True)View Code
常用引數
引數 | 說明 | 取值 |
default | 給引數設定預設值, 如果不傳遞該引數會使用預設值 | str/int |
required | 是否必須傳遞,預設False,如果設定為True, 不傳遞會返回400 | True / False |
lacation | 設定引數提取的位置 | 字串, args/form/json/files/headers/cookies |
type | 設定引數的轉換型別(型別轉換 & 格式校驗) | 函式引用, int/內建型別/自定義函式 |
程式碼示例:
- default、required、location
from flask import Flask from flask_restful import Resource, Api from flask_restful.reqparse import RequestParser app = Flask(__name__) api = Api(app) class DemoResource(Resource): def post(self): parser = RequestParser() parser.add_argument('name', required=True, location='json') parser.add_argument('age', default=10) args = parser.parse_args() print(args.name) print(args.age) return {'foo': "post"} api.add_resource(DemoResource, '/') if __name__ == '__main__': app.run(debug=True)View Code
- type
from flask import Flask from flask_restful import Resource, Api from flask_restful.reqparse import RequestParser from flask_restful.inputs import * app = Flask(__name__) api = Api(app) # 自定義函式進行引數校驗和轉換 def func1(value): # 必須定義形參來接收傳遞來的引數 if re.match(r'^user:', value): return value[5:] # 轉換完, 還需要將結果返回 else: raise ValueError('age引數格式錯誤') # 校驗失敗, 會將錯誤資訊以json形式返回 class DemoResource(Resource): def put(self): parser = RequestParser() parser.add_argument('name') # parser.add_argument('age', type=int) # 轉為int型別 # parser.add_argument('age', type=boolean) # 轉為bool型別 1/0 true/false # parser.add_argument('age', type=date) # 日期 轉為datetime型別 YYYY-mm-dd # parser.add_argument('age', type=datetime_from_iso8601) # 時間 轉為datetime型別 2012-01-01T23:30:00+02:00 # parser.add_argument('age', type=int_range(5, 10)) # 轉為int型別 限定範圍[5, 10] # parser.add_argument('age', type=regex(r'^1[3-9]\d{9}$')) # 要求匹配正則 parser.add_argument('age', type=func1) # 自定義函式 args = parser.parse_args() print(args.name) print(args.age) print(type(args.age)) return {'foo': 'put'} api.add_resource(DemoResource, '/') if __name__ == '__main__': app.run(debug=True)View Code
3、響應
3.1、序列化
flask-restful
通過marshal
函式 來完成序列化處理操作步驟如下:
- 定義序列化規則
序列化規則 = {欄位名: 序列化型別}
marshal
函式 按照序列化規則 將模型物件轉為字典序列化後的字典 = marshal(模型物件, 序列化規則)
- 定義序列化規則
常用序列化型別:
序列化型別 | 說明 |
String | 可轉換 字串型別屬性 |
Integer | 可轉換 整數型別屬性 |
Float | 可轉換 浮點數型別屬性 |
List | 可轉換 列表型別屬性, 要求列表中元素型別唯一 |
Nested |
可轉換 字典型別屬性 |
from flask import Flask from flask_restful import Resource, Api, marshal, fields app = Flask(__name__) api = Api(app) class User: # 定義模型類 def __init__(self): self.name = 'zs' self.age = 20 self.height = 1.8 self.scores = [80, 90] self.info = { 'gender': True } fields = { # 序列化規則 'username': fields.String(attribute='name'), # 指定對應的模型屬性 'age': fields.Integer(default=10), # 設定預設值 'height': fields.Float, 'scores': fields.List(fields.Integer), # 元素型別唯一 'info': fields.Nested({'gender': fields.Boolean}) } class DemoResource(Resource): def get(self): user1 = User() # marshal函式可以按照指定的序列化規則將 模型物件 轉為 字典 return marshal(user1, fields, envelope='data') api.add_resource(DemoResource, '/') if __name__ == '__main__': app.run(debug=True)View Code
拓展:序列化還有一種裝飾器形式 marshal_with
, 使用裝飾器形式則檢視可以直接返回模型物件
from flask import Flask from flask_restful import Resource, Api, marshal_with, fields app = Flask(__name__) api = Api(app) class User: # 定義模型類 def __init__(self): self.name = 'zs' self.age = 20 self.height = 1.8 self.scores = [80, 90] self.info = { 'gender': True } fields = { # 序列化規則 'username': fields.String(attribute='name'), # 指定對應的模型屬性 'age': fields.Integer(default=10), # 設定預設值 'height': fields.Float, 'scores': fields.List(fields.Integer), # 元素型別唯一 'info': fields.Nested({'gender': fields.Boolean}) } class DemoResource(Resource): method_decorators = {'post': [marshal_with(fields)]} def post(self): user1 = User() # 如果設定了marshal_with裝飾器, 可以直接返回模型物件 return user1 api.add_resource(DemoResource, '/') if __name__ == '__main__': app.run(debug=True)View Code
實際開發中, 考慮到序列化的複雜性, 部分公司還會採用 自定義方法 的方式來完成序列化處理
from flask import Flask from flask_restful import Resource, Api app = Flask(__name__) api = Api(app) class User: def __init__(self): self.name = 'zs' self.age = 20 self.height = 1.8 self.scores = [80, 90] self.info = { 'gender': True } def to_dict(self): # 自定義模型轉換方法 return { 'name': self.name, 'age': self.age } class DemoResource(Resource): def put(self): user1 = User() return user1.to_dict() api.add_resource(DemoResource, '/') if __name__ == '__main__': app.run(debug=True)View Code
3.2、自定義JSON
實際開發中, 返回的JSON資料中除了包含基礎資料, 往往還需要設定一些 統一的外層包裝, 以便前端進行更好的解析處理, 如:
{ "message": "ok", // 外層包裝 "code": 200, // 外層包裝 "data": { // 基礎資料 "name": "張三", "age": 20 } }
flask-restful
提供了api.representation()
裝飾器方法, 允許開發者自定義返回的資料格式在實現自定義JSON格式時, 可以參考
flask-restful
預設轉換函式 (字典轉json資料) 的原始碼預設轉換函式的原始碼:
# flask_restful/representations/json.py def output_json(data, code, headers=None): """Makes a Flask response with a JSON encoded body""" settings = current_app.config.get('RESTFUL_JSON', {}) # If we're in debug mode, and the indent is not set, we set it to a # reasonable value here. Note that this won't override any existing value # that was set. We also set the "sort_keys" value. if current_app.debug: settings.setdefault('indent', 4) settings.setdefault('sort_keys', not PY3) # always end the json dumps with a new line # see https://github.com/mitsuhiko/flask/pull/1262 dumped = dumps(data, **settings) + "\n" resp = make_response(dumped, code) resp.headers.extend(headers or {}) return respView Code
from collections import OrderedDict from json import dumps from flask import Flask, current_app, make_response, Response from flask_restful import Resource, Api from six import PY3 app = Flask(__name__) api = Api(app) @api.representation('application/json') # 指定響應形式對應的轉換函式 def output_json(data, code, headers=None): """自定義json形式""" # 根據flask內建配置, 進行格式處理(縮排/key是否排序等) settings = current_app.config.get('RESTFUL_JSON', {}) if current_app.debug: settings.setdefault('indent', 4) settings.setdefault('sort_keys', not PY3) # 新增json外層包裝 if 'message' not in data: # 判斷是否設定了自定義的錯誤資訊 data = { 'message': 'ok', 'data': data } # 字典轉json字串 dumped = dumps(data, **settings) + "\n" # 構建響應物件 resp = make_response(dumped, code) resp.headers.extend(headers or {}) return resp class DemoResource(Resource): def get(self): return {'foo': "get"} def post(self): return {'message': 'parameter error: name', "data": None} api.add_resource(DemoResource, '/') if __name__ == '__main__': app.run(debug=True)View Code
實際開發中, 可能還會展開裝飾器來設定自定義響應格式
def output_json(data, code, headers=None): """自定義json形式""" # 根據flask內建配置, 進行格式處理(縮排/key是否排序等) settings = current_app.config.get('RESTFUL_JSON', {}) if current_app.debug: settings.setdefault('indent', 4) settings.setdefault('sort_keys', not PY3) # 新增json外層包裝 if 'message' not in data: # 判斷是否設定了自定義的錯誤資訊 data = { 'message': 'ok', 'data': data } # 字典轉json字串 dumped = dumps(data, **settings) + "\n" # 構建響應物件 resp = make_response(dumped, code) resp.headers.extend(headers or {}) return resp # 展開裝飾器的形式設定自定義響應形式 api.representation('application/json')(output_json)View Code