経緯
業務でLINEのAPIで画像をアップロードする必要があり、ドキュメントを確認すると「RFC-1867 に従い、フォーム形式でファイルをアップロードします。」とあったので調べてみるとinputタグでsubmitでデータを送信する要領でrequestsを作成しました
実際に叩いていたAPIは↓の画像アップロードAPI
参考:https://developers.worksmobile.com/jp/docs/file-upload
上記のドキュメントを読んで作ったPythonのコードはこんな感じ
# ローカルの画像のPATH file_path = 'sample.png' # APIのアクセストークン access_token ="xxxxxxxxxxxxxxxxxxxxxxxx" # 画像データを値を載せたdictを作成 files = {"image_file": open(file_path, 'rb'),'resourceName': "sample.png"} # リクエストヘッダー headers = { 'Content-Type':'multipart/form-data', 'Authorization': 'Bearer ' + access_token, } # postリクエスト送信 r = requests.post(upload_url, files=files, headers=headers) # レスポンス確認 print(r, r.text)
APIは基本的にContent-Typeが「application/json」、一部「application/x-www-form-urlencoded」や「multipart/form-data」が必要という感じだったので、ヘッダーにContent-Typeとして’multipart/form-data’と指定していましたが、bearer認証のaccess_tokenの値は適切なものを使用しているにも関わらず401のAuthentication error(認証エラー)が起きました。
原因と解決法
原因は認証方法やaccess_tokenの値ではなく、Content-Typeの方にありました。
上記のコードのheadersでは以下のようなヘッダーの辞書が生成されます。
{
'Authorization': 'Bearer xxxxx',
'Content-Type': 'multipart/form-data',
}
このヘッダーを使用してしまうとContent-Typeにboundaryが付与されません。
boundaryとは、メッセージの複数パートの境界を囲むために使用するものらしく、multipart/form-dataのデータを送信する際に必要となるものです
Pythonのクライアント通信ライブラリであるrequestsの場合はboundaryの設定はこちらが一々何か計算して設定する必要はなく勝手に付与してくれるみたいなのですが、上記のコードでははContent-Typeを単なる「multipart/form-data」で設定してしまったため、requestsが付与したデフォルトのContent-Typeが上書きされ、boundaryが無い状態になってしまい401の認証エラーが起こっていました。
これを解決する方法は簡単で、単純にheadersからContent-Typeを削除するだけです。
# リクエストヘッダー headers = { 'Content-Type':'multipart/form-data', 'Authorization': 'Bearer ' + access_token, }
↓
# リクエストヘッダー headers = { 'Authorization': 'Bearer ' + access_token }
これでエラーは発生しなくなり、正常にデータが送れるようになりました。知っている人からすれば当たり前では、と思われそうですが普段フォーム経由でデータを送信しているとフォーム送信について詳しいHTTP通信の知識がなくハマりました。。。
.
結論
Pythonのrequests経由で「multipart/form-data」形式のリクエストを送信する場合はheadersにContent-Typeを渡してはいけない
関連記事:【Python】Flaskのjsonifyで簡単なAPIサーバーを作成する
コメント
[…] 関連記事:【Python】requests経由でmultipart/form-dataのデータを送信する際に認証エラーが起こった話 […]