flaskはpythonでウェブアプリケーションを作るためのライブラリである。ここでは、flaskを使ううえでの基本的な方法をまとめる。
インストール
pip install flask
導入
まず最初に、最も簡単な"Hello World"を返すAPIを作る。
from flask import Flask
app = Flask(__name__)
@app.route('/')
def root():
return "Hello World!"
if __name__ == "__main__":
app.run()
- 関数
@app.route(url)でデコレートすると、そのurlにアクセスすると、その関数が実行されるようになる。
python app.py
上で作成したapp.pyを実行すると、ウェブサーバーが起動する。ブラウザでhttp://localhost:5000/にアクセスすると、画面にHello World!が表示されるようになる。
開発時の設定
from flask import Flask
app = Flask(__name__)
@app.route('/')
def root():
return "Hello World!"
if __name__ == "__main__":
app.run(debug=True,host='localhost',port=5000)
-
mainを上のように変える。 -
debug=Trueを渡すと(デバッグモード)、サーバーが起動している時に、app.pyや後述するhtmlファイルなどを変更した時に、自動的にロードされる。debugの初期値はFalse。 -
host・portは自由に決めれる。portの初期値は5000。
外部からアクセスできるようにする。
from flask import Flask
app = Flask(__name__)
@app.route('/')
def root():
return "Hello World!"
if __name__ == "__main__":
app.run(debug=False,host='0.0.0.0',port=5000)
-
mainを上のように変える。 -
host=localhostの時にはローカルからしかアクセスできない。外部からアクセスできるようにするには、host="0.0.0.0"。 - セキュリティの理由から
debug=Falseにする。
ブラウザも同時に起動する
import threading,webbrowser
from flask import Flask
app = Flask(__name__)
@app.route('/')
def root():
return "Hello World!"
if __name__ == "__main__":
threading.Timer(1.0, lambda: webbrowser.open('http://localhost:5000') ).start()
app.run(debug=False)
-
mainでブラウザーを開くコマンドを追加する。 -
app.pyを実行すると同時にブラウザーが立ち上がり、ページが表示される。 -
app.run()にdebug=Trueを渡すと、コードを変更するたびにmainが再実行されるため、その度にブラウザーが追加で開いてしまう。それを防ぐためdebug=Falseを渡している.
デバッグモード(debug=True)でブラウザーを自動で開くようにしたい時には、mainは
if __name__ == "__main__":
app.run(debug=True)
にして、
# !/bin/sh
( sleep 2; open http://localhost:5000/ ) &
python app.py
のようなシェルスクリプトを実行するような方法がある。
htmlファイルを返す
from flask import Flask,render_template
app = Flask(__name__)
@app.route('/')
def main():
return render_template('index.html')
if __name__ == "__main__":
app.run()
<html>
<body>
Hello World!
</body>
</html>
- htmlファイルを返す時には
render_template(file)を用いる。 - htmlファイル(ここでは
index.html)はapp.pyと同じ場所にtemplatesというディレクトリを作り、その中に配置する。 -
http://localhost:5000/にブラウザからアクセスするとindex.htmlがレンダリングされ、この場合は"Hellow World!"が表示される。
CSS・JavaScriptファイル
- CSSファイルやJavaScriptファイルはディレクトリ
./staticに配置する。画像もここに配置する。 - htmlファイルからは、それぞれ
<head>
<script src="/static/script.js"></script>
<link rel="stylesheet" href="/static/style.css">
</head>
のように呼び出す。
GETリクエスト
- ここでは
/getのアドレスにGETリクエストを受けて、{"message":"Hello World!"}というjsonオブジェクトを返すapiを考える。
from flask import Flask,request,jsonify,render_template
app = Flask(__name__)
@app.route('/')
def index_html():
return render_template('index.html')
@app.route('/get',methods=['GET'])
def get():
return jsonify({'message':'Hello world!'})
if __name__ == '__main__':
app.run()
- デコレータ
@app.route(url,methods)でリクエストを受け入れるurlとmethodsを指定。 -
jsonify()はオブジェクトを文字列になおし、適切なレスポンスを生成する関数である。
<html>
<body>
<button onclick='get();'>get</button>
<script>
function get(){
fetch('/get').then(
response => response.json()
).then(
json => { document.body.innerHTML += JSON.stringify(json); }
).catch(
e => console.error(e)
);
}
</script>
</body>
</html>
- ボタンを押すと、返ってくるJSONを文字列として表示される。
fetchが成功したい時に何か処理を行いたい時には、次のようにすれば良い。
fetch('/get').then(
response => (
if (response.ok){
//成功時に行ないたい処理
} else {
throw new Error()
}
).catch(
e => console.error(e)
);
POSTリクエスト
- ここでは
/postのアドレスにPOSTリクエストを受けて、同じJSONをそのまま返すapiを考える。
from flask import Flask,request,jsonify,render_template
app = Flask(__name__)
@app.route('/')
def index_html():
return render_template('index.html')
@app.route('/post',methods=['POST'])
def post(): #送られてきたjsonをそのまま返す。
req = request.json
return jsonify(req)
if __name__ == '__main__':
app.run()
- POSTリクエストで送られてきたJSONは
request.jsonで得られる。
<html>
<body>
<button onclick='post();'>post</button>
<script>
function post(){
fetch('/post', {
method: "POST",
headers: { "Content-Type": "application/json"},
body: JSON.stringify({"message": "Hello World!!!"})
}).then(
response => response.json()
).then(
json => { document.body.innerHTML += JSON.stringify(json); }
).catch(
e => console.error(e)
);
}
</script>
</body>
</html>
- ここでは、ボタンを押すと
{'message':'Hello World!!!'}のJSONをデータとして持つPOSTリクエストを送る。同じJSONが返ってくるのでそれを表示する。
画像データを返すAPI
サーバーはデータを受信し、matplotlibでグラフを作成し、その画像データを返すようなAPIを考える。ここでは画像データをdataURLに変換してデータを返す。dataURLは画像データをurlとして表現したものである。
from flask import Flask,render_template,jsonify,request
import matplotlib
matplotlib.use('agg')
from matplotlib import pyplot as plt
from matplotlib.backends.backend_agg import FigureCanvasAgg
import io,base64
app = Flask(__name__)
@app.route('/')
def main():
return render_template('index.html')
@app.route('/image',methods=['POST'])
def image():
data = request.json
x = data['x']
y = data['y']
#図を作成
plt.figure(figsize=(3,3),dpi=100)
plt.plot(x,y,'k*-',markersize=10)
#図をdataURLに変更
canvas = FigureCanvasAgg(plt.gcf())
png_output = io.BytesIO()
canvas.print_png(png_output)
img_data = base64.b64encode(png_output.getvalue()).decode('utf-8')
dataurl = 'data:image/png;base64,'+img_data
return jsonify({'img_data':dataurl})
if __name__ == "__main__":
app.run()
- まず、サーバー上での図の描画になるので、下のコマンドを
matplotlib関連のライブラリのimportの一番最初に行うことに注意する。
import matplotlib
matplotlib.use('agg')
-
image()ではmatplotlibでデータからグラフを作成し、それをdataURLに変換し、それを返している。
<html>
<body>
<button onclick="image();">img</button>
<div id='img'></div>
<script>
function image(){
fetch('/image', {
method: "POST",
headers: { "Content-Type": "application/json"},
body: JSON.stringify({"x":[0,1,2,3,4,5], "y":[5,2,4,0,3,8]})
}).then(
response => response.json()
).then(
json => { document.getElementById('img').innerHTML = `<img src="${json['img_data']}">`; }
).catch(
e => console.error(e)
);
}
</script>
</body>
</html>
- imgボタンをクリックすると、データ
x=[0,1,2,3,4,5]、y=[5,2,4,0,3,8]をサーバーに送る。 - レスポンスが返ってきたら、dataURLをimgタグのsrcに入れれば画像が表示される。
クロスオリジンリクエスト(COR):別のドメインからのアクセス
デフォルトでは別のドメインからのアクセスは制限されている。これを許可するためにはflask-corsのパッケージを使う。
pip install flask-cors
from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
CORS(app)
@app.route('/')
def root():
return "Hello World!"
if __name__ == "__main__":
app.run()
- 単に
CORSをappに適用するだけでOK。
ユーザー認証(ダイジェスト認証)
認証のためにflask-httpauthのパッケージを用いる。
pip install flask-httpauth
from flask import Flask
from flask_httpauth import HTTPDigestAuth
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret key here'
auth = HTTPDigestAuth()
users = {
'Michael': 'pw_m',
'Smith': 'pw_s'
}
@auth.get_password
def get_pw(username):
if username in users:
return users.get(username)
return None
@app.route('/')
@auth.login_required
def root():
return "Hello World!"
if __name__ == "__main__":
app.run()
- ユーザー名を与えられた時に、そのユーザーが存在するならば、そのユーザーのパスワードを返し、ユーザーが存在しないならば、
Noneを返す関数を作り(get_pw)、それを@auth.get_passwordでデコレートする。 - ここではユーザは'Michael'と'Smith'二人存在し、パスワードはそれぞれ'pw_m'、'pw_s'であるとする。
- 認証が必要な関数を
@auth.login_requiredでデコレートする。 - アクセスすると認証画面が最初に表れ、正しいユーザー名・パスワードを入力すると、"Hello World"が表示される。