背景
PythonでコーディングしていくうえでRequestsモジュールを利用し、Webコンテンツを取得したりWebAPIを叩いたりするケースがあります。
これまではその時の実際のrequestの内容までは、あまり確認したいというケースはありませんでした。
そりゃぁそうです。自分でHeaderやBodyのデータを自分でセットしているんですから、それを確認すれば良いんです。
そしてWebブラウザ上でJavaScriptとかから送信している場合は、Chromeのデベロッパーツールを使えばPOST通信時に送信したパラメータは簡単に確認できます。
今回、訳あってRequestsモジュールで実際に送っているデータを確認する方法を探したのですが、無いのです! 有ってもおかしくはない機能かと思うのですが、見つかりません!!
本当に無いの?? 私が探し当てられなかっただけなの??
Requests.session.get('body')
こんな感じで、取得できたら良かったのですが…
できなかった…
(だれか、知っている人がいらっしゃいましたら… おせーてくださいませ)
そんなわけで…
まぁ、無いなら自分のWebサーバーに向けてrequestして、そのデータを見れば良いでしょう…
と、思ったのですが、なんだかわざわざFlaskを使ってアプリを書くのも面倒で、世の中の人はどう実現しているのか探ってみたところ、
もう… これでしょ! という事例が見つかりましたぁ。
『curlのGET,POSTのデータを確認』https://qiita.com/tkj/items/210a66213667bc038110
を参考にさせてもらいました。ありがとうございます。
この方法であれば、わざわざサーバーを使う必要なく、手元(ローカル)の環境で確認ができます。簡単簡単!!
やってみましょう。
なぜそこまでして、requestの内容を確認したかったかというと…
業務の自動化を図るために、社内のWebシステムにファイルをアップロードする仕組みを作っています。この仕組みは、WebAPIとして稼働していて、ビジネスロジックを担当するアプリケーションが、BLOBでファイルをこのWebAPIに渡してくれると、このWebAPIが社内のWebシステムにファイルをアップロードするというものです。
社内のWebシステムにはいろいろ"仕来り"がありますので、その辺をWebAPIが担当し、アプリケーションは簡単に社内のWebシステムにアップロードできるようなカラクリにしています。
この時、社内のWebシステムは"multipart/form-data"で受けるようになっています。
なので、ローカルのファイルであれば、こんな感じでアップロードできることは確認しています。
import Requests
URL = 'https://社内のWebシステム.com/アプリ/アップロード/'
FILES = {'{{FORM_NAME}}': open('アップロードするファイルのPATH', 'rb')}
response = requests.post(URL, files=FILES)
しかし、WebAPIはビジネスロジック(アプリケーション)から受けたBLOBを、テンポラリファイルとして保存はしたくはないのです。
せっかくWebAPIはBLOBとして受けているのですから、そのまま io.BytesIO(ファイルの内容) でファイルとして扱いたいじゃないですか??
import Requests
URL = 'https://社内のWebシステム.com/アプリ/アップロード/'
FILES = {'{{FORM_NAME}}': io.BytesIO(ファイルの内容)}
response = requests.post(URL, files=FILES)
open('アップロードするファイルのPATH', 'rb')
でファイルオブジェクトを指定しているんだから、io.BytesIO(ファイルの内容)
も同じでしょ! と思ったんですがね。
でも、これだと… なんだか上手くいかないんですよぉ…
なので表題の件である、「requestの内容」を確認し、open('アップロードするファイルのPATH', 'rb')
とio.BytesIO(ファイルの内容)
での、requestの違いを確認したかったのです!
参考にさせていただいた『curlのGET,POSTのデータを確認』では、ファイルのようなバイナリデータをrequestするとちょっと都合が悪かったので、その辺なんかに手を加えています。
import http.server
from urllib.parse import urlparse, parse_qs
class MyHandler(http.server.BaseHTTPRequestHandler):
def do_POST(self):
self.make_data()
def do_GET(self):
self.make_data()
def make_data(self):
req_method = self.command
req_parsed = urlparse(self.path)
req_params = '{}'.format(parse_qs(req_parsed.query))
req_head = '\n{}'.format(self.headers)
length = self.headers.get("content-length")
content_len = int(length) if length else 0
_req_body = self.rfile.read(content_len)
req_body = '{}'.format(_req_body)
# レスポンスデータ生成
body = '== リクエストデータ ====================\n'
body += 'method: ' + req_method + '\n'
body += 'header: ' + req_head + '\n'
body += 'params: ' + req_params + '\n'
body += 'body : ' + req_body + '\n'
body += '================================\n'
# サーバーの標準出力
print('== リクエストデータ ====================')
print('【METHOD 】', req_method)
print('【HEADERS】', req_head)
print('【PARAMS 】', req_params)
print('【 BODY 】', req_body)
print('================================')
# レスポンス
self.send_response(200)
self.send_header('Content-type', 'text/html; charset=utf-8')
self.send_header('Content-length', len(body.encode()))
self.end_headers()
self.wfile.write(body.encode())
host = '0.0.0.0'
port = 5001
httpd = http.server.HTTPServer((host, port), MyHandler)
print('ポート:%s' % port)
httpd.serve_forever()
このサーバーを起動すると…
% python echoRequestsHttpServer.py
ポート:5001
そして、上手くアップロードできない、対象のrequestをこのサーバーに向けてrequestすると…
== リクエストデータ ====================
【METHOD 】 POST
【HEADERS】
user-agent: Takky_API
Accept-Encoding: gzip, deflate
Accept: application/json, text/javascript, */*; q=0.01
Connection: keep-alive
Content-Length: 1163
Content-Type: multipart/form-data; boundary=0c0acc9c1affeddfa38401805492e8f5
【PARAMS 】 {}
【 BODY 】 b'--0c0acc9c1affeddfa38401805492e8f5\r\n ※みやすくするため改行してます
Content-Disposition: form-data; name="{{FORM_NAME}}";
※ココ → filename="{{FORM_NAME}}"\r\n\r\n
# ^^^^^^^^
"ファイルの内容"\r\n\r\n
--0c0acc9c1affeddfa38401805492e8f5--\r\n'
================================
192.168.40.136 - - [15/Aug/2022 18:14:23] "POST / HTTP/1.1" 200 -
問題なくアップロードできる、ローカルファイルをアップロードするrequestだと…
== リクエストデータ ====================
【METHOD 】 POST
【HEADERS】
user-agent: Takky_API
Accept-Encoding: gzip, deflate
Accept: application/json, text/javascript, */*; q=0.01
Connection: keep-alive
Content-Length: 1145
Content-Type: multipart/form-data; boundary=c53e52730b147a1a87e3452b37f5dc27
【PARAMS 】 {}
【 BODY 】 b'--c53e52730b147a1a87e3452b37f5dc27\r\n ※みやすくするため改行してます
Content-Disposition: form-data; name="{{FORM_NAME}}";
※ココ → filename="{{アップロードするファイルのファイル名}}"\r\n\r\n
# ^^^^^^^^^^^^^^^^^^
"ファイルの内容"\r\n\r\n
--c53e52730b147a1a87e3452b37f5dc27--\r\n'
================================
192.168.40.136 - - [15/Aug/2022 18:13:21] "POST / HTTP/1.1" 200 -
わかりましたぁ!!!!!!
ちゃんと、確認できれば簡単にわかることでした。
"※ココ →"で示されているように、open('アップロードするファイルのPATH', 'rb')
でファイルを渡している場合は、bodyの「filename」に open() したファイル名がセットされていますが、io.BytesIO(ファイルの内容)
で渡している場合には「filename」に "FORM_NAME" と同じ値が渡されています。
そうですねぇ… 「filename」は渡さなきゃイカンですよね!?
なので、「filename」も渡してあげるように修正します。
URL = 'https://社内のWebシステム.com/アプリ/アップロード/'
FILES = {'{{FORM_NAME}}': ('アップロードするファイルのファイル名',io.BytesIO(ファイルの内容))}
# ^^^^^^^^^^^^^^^^^^ ^
response = requests.post(URL, files=FILES)
これで、WebAPIは社内のWebシステムに、テンポラリファイルを介すことなくファイルをアップロードすることができました。
やはり、「百聞は一見にしかず」です。
横着をせずにちゃんと状況を"正確に"把握することが、いろいろ近道につながります。