Flask (Python3)でのformおよびajax通信等から送信されたPOSTデータやファイルの受け取り
公開日:
更新日:
Flaskを使い、formやajax通信のPOSTデータを受け取る方法をご紹介します。(というより単なる個人的備忘録です)
Flaskのサーバー機能やルーティングンの詳細には触れませんが、以下のサンプルでは /api/rcv_postdata/<id>/ のようにidパラメータが入るケースを想定して記述しています。
POSTデータは、formのように綺麗に整形されて入ってくるものとjson形式でbodyにセットされてくるパターンがあります。
Flaskでは、request.formやrequest.dataでどちらも簡単に取得することができます。
ここではついでにmultipartでの添付ファイルを受け取る場合の処理も記述しておきます。
下記処理ではformからのデータを確認して、なければbody側のデータを確認するように書いています。
一通りのデータは文字列で入ってくるため、その先に何か処理を書く際に使い勝手が良いように型変換を行っています。
isdecimal()で数値判定をしてしまうと、Pythonの場合は全角だったり漢数字もTrueを返してしまいます。
さらにマイナスが付くとFalseになったりするので、微妙に使い勝手がよくありません。
半角文字であることを判定するためのisascii()と組み合わせて使おうかと思ったのですが、Python3.7からで環境によって使えなかったりするので、正規表現でintとfloatの判定をしています。
小数点はただ見つけているだけですが、本当は1個だけという処理にした方が良かったかもしれません。
ちなみに、isascii()はアスキー文字の判定のため、マイナス記号や小数点が入っていてもTrueが返ります。
from datetime import datetime from flask import Flask, request from werkzeug.utils import secure_filename import ast import datetime as dt import os import re import time upload_dir = '/mnt/ssd/uploads' app = Flask(__name__) @app.route("/api/rcv_postdata/<id>/", methods=['POST']) def rcv_postdata(id): postdata = {} # formからのPOST, multipartデータはImmutableMultiDictで来る print(request.form) print(request.files) # ajax等でのjsonで送信されたPOSTデータはbody部に入るのでByteString print(request.data) if len(request.form) > 0: postdata = request.form.to_dict(flat=True) elif len(request.data) > 0: try: postbody = request.data.decode('utf-8') postdata = ast.literal_eval(postbody) except: pass # 添付ファイル filename = '' if len(request.files) > 0: for key in request.files: print(request.files[key]) file = request.files[key] if file.filename != '': filename = secure_filename(file.filename) file.save(os.path.join(upload_dir, filename)) # filenameやupload_dirはpostdataに入れておくと便利かも if filename != '': postdata['filepath'] = os.path.join(upload_dir, filename) # URLに付与されたidも後段で使いやすいようpostdata配列に入れておくが、idという項目がPOSTされる場合はここで上書きしてしまうので、キー名は使い勝手が良いように書き換える postdata['id'] = id # formからのデータは文字列になっているため、正しい型に変換する for key in postdata: if key == 'timestamp': # datetime項目は項目名とフォーマットを調べて記述する try: postdata[key] = datetime.strptime(postdata[key], '%Y-%m-%d %H:%M:%S') # 好きな形式の文字列に変換したい場合はさらに変換する postdata[key] = postdata[key].strftime('%Y-%m-%dT%H:%M:%S.%f') # or isoformat # postdata[key] = postdata[key].isoformat() except: pass elif isinstance(postdata[key], str): if re.search('^(\-?[0-9\.]+)$', postdata[key]): if postdata[key].find('.') != -1: try: postdata[key] = float(postdata[key]) except: pass else: try: postdata[key] = int(postdata[key]) except: pass elif postdata[key].find('[') == 0 and postdata[key][-1] == ']': # 文字列としてlistが来た場合。dictも対応する場合は同様に{}を設定する try: postdata[key] = json.loads(postdata[key]) except: pass else: pass elif isinstance(postdata[key], (int, float)): # 元からint型やfloat型の項目 pass else: # 該当しないものはとりあえずstr関数で文字列化しておく postdata[key] = str(postdata[key]) # なんとなく型変換できているので、この先の各種処理で触りやすい形となっている print(postdata) return if __name__ == "__main__": app.run(host='0.0.0.0',port=5000,debug=True)