Flask設定返回json格式資料
問題描述
在Flask中直接返回list
或dict
是不行的,如
from flask import Flask
app = Flask(__name__)
@app.route('/')
def root():
t = {
'a': 1,
'b': 2,
'c': [3, 4, 5]
}
return t
if __name__ == '__main__':
app.debug = True
app.run()
這樣訪問會直接提示
TypeError: 'dict' object is not callable
其原因是Flask並不會將list或dict預設轉換為json格式。
解決方法
HTTP返回json格式資料主要有兩個方面:
1. 資料本身為json格式;
2. Content-Type
宣告為json格式。
使用標準庫json
比較常見的是採用標準庫json進行格式轉換:
from flask import Flask
import json
app = Flask(__name__)
@app.route('/')
def root():
t = {
'a': 1,
'b': 2,
'c' : [3, 4, 5]
}
return json.dumps(t)
if __name__ == '__main__':
app.debug = True
app.run()
這樣當訪問時即能夠正常得到json資料。但這麼做有一個缺點,就是HTTP返回的Content-Type
仍然是text/html
,即HTTP認為內容是HTML。
宣告Content-Type為json格式
在上面的解決方法上作一個加強,手動指定其Content-Type
為application/json
,通常採用的是修改Flask中的Response
模組:
from flask import Flask, Response
import json
app = Flask(__name__)
@app.route('/')
def root():
t = {
'a': 1,
'b': 2,
'c': [3, 4, 5]
}
return Response(json.dumps(t), mimetype='application/json')
if __name__ == '__main__':
app.debug = True
app.run()
這樣不僅HTTP返回的內容是json,而且返回的Content-Type
也是application/json
了。
使用Flask的jsonify模組
實際上flask已經為json準備了專門的模組:jsonify
。jsonify
不僅會將內容轉換為json,而且也會修改Content-Type
為application/json
。
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/')
def root():
t = {
'a': 1,
'b': 2,
'c': [3, 4, 5]
}
return jsonify(t)
if __name__ == '__main__':
app.debug = True
app.run()
自定義Flask的Response,使用force_type()
對於某些特殊的情況,可能並不想每個返回json資料的方法都使用jsonify()
包起來,那有沒有什麼“非侵入式”的方法實現jsonify()
的功能呢?其實是有的,不過這個方法相對比較高階。
Flask返回的內容實際是Response
物件,return
語句的內容實際是交給Response
處理後才輸出由HTTP返回的;也就是說,之前直接返回dict
報錯TypeError: 'dict' object is not callable
也是Response
乾的。那麼只需要在Response
處理如dict
等“非法”資料是,告訴Response
該怎麼做就好了,這裡就是用到了其force_type()
方法了,所有不能處理的資料,都由force_type()
方法嘗試處理後,再決定報錯或通過。直接看例子吧。
from flask import Flask, Response, jsonify
class MyResponse(Response):
@classmethod
def force_type(cls, response, environ=None):
if isinstance(response, (list, dict)):
response = jsonify(response)
return super(Response, cls).force_type(response, environ)
app = Flask(__name__)
app.response_class = MyResponse
@app.route('/')
def root():
t = {
'a': 1,
'b': 2,
'c': [3, 4, 5]
}
return t
if __name__ == '__main__':
app.debug = True
app.run()
或者還可以以繼承的方式來實現自定義Response
,如:
from flask import Flask, Response, jsonify
class MyResponse(Response):
@classmethod
def force_type(cls, response, environ=None):
if isinstance(response, (list, dict)):
response = jsonify(response)
return super(Response, cls).force_type(response, environ)
class MyFlask(Flask):
response_class = MyResponse
app = MyFlask(__name__)
@app.route('/')
def root():
t = {
'a': 1,
'b': 2,
'c': [3, 4, 5]
}
return t
if __name__ == '__main__':
app.debug = True
app.run()