概要
QRコードを発行するWebAPIをデプロイし、Power Automateのカスタムコネクタから接続します。全3回を予定しています。
- 「QRコードを発行するAPIをつくってみる」
- 「WebAPIとしてデプロイ」
- 「Power Automateのカスタムコネクタから接続」
1回目はQRコードを発行するAPIを作るところまで行います。 以前オンプレでQRコードを作成するためにPythonのqrcodeライブラリーを使用する方法を知りました。Pythonを利用したWebAPIについて調べるとFastAPIがDocsやチュートリアルが日本語化されていて、はじめやすそうに感じました。今回はこれらを組み合わせます。
環境
- Visual Studio Code 1.66.2
- Python 3.9.12
- FastAPI 0.75.2
- qrcode 7.3.1
- Pillow 9.1.0
- uvicorn 0.17.6
- Power Apps 開発者プログラム (今回は使いません)
- Power Automate 試用ライセンス(今回は使いません)
注意事項
- 前提条件としてVisual Studio Codeと各種機能拡張、Pythonのインストールが必要です。
- デプロイ先はFastAPIのチュートリアルにあるDetaを利用します。
- Detaで利用できるPythonのバージョンは3.7、3.8、3.9となっています。
- 「QRコード」はデンソーウェーブの登録商標です。
- 仕様についてはデンソーウェーブのHPを参考にしてください。
- これまでWebAPIを利用することはあっても作ったことはなかったので、学習した備忘録です。
- 自己責任でお願いいたします。
- 2022年5月の情報です。
お手本の確認
今回やりたいのは、WebAPIからQRコードの画像をレスポンスとして受け取ることです。とりあえずいつ無くなるかわからないGoogle Charts Infographics QR Codesをお手本として確認しておきます。
GETでクエリパラメーターをリクエストしているのがURLから確認できます。なかの仕組みはわかりませんがQRコードの画像をレスポンスとして返してくれます。
https://chart.googleapis.com/chart?cht=qr&chs=144x144&chl=test2
FastAPIとqrcodeを組み合わせることで実現できそうです。(思い込みで始めました💦)
環境構築
venvの準備からqrcodeとpillowインストール
-
Visual Studio Codeから新規でフォルダーを開きます。フォルダー名を「QRAPI」にしました。今回はMacを使用していますがWindowsの場合はpyhton3をpyにして実行します。(公式版のインストールでパスを通してある場合)
-
Pythonのバージョン確認しておきます。
python3 --version
-
venvを作成します。今回は環境名もvenvにしました。
python3 -m venv 仮想環境名
-
venvを実行します。`. venv/bin/activate``
-
pillowをインストールします。
python3 -m pip install pillow
-
qrcodeをインストールします。
python3 -m pip install qrcode
-
インストールを確認します。
pyhton3 -m pip list
FastAPIとUvicornのインストール
-
FastAPIのインストール
FastAPI は、Pythonの標準である型ヒントに基づいてPython 3.6 以降でAPI を構築するための、モダンで、高速(高パフォーマンス)な、Web フレームワークです。
python3 -m pip install fastapi
-
Uvicornのインストール
Uvicorn is an ASGI (Asynchronous Server Gateway Interface) web server implementation for Python.
とあるように非同期対応のPython用Webサーバです。開発時の動作確認に使用します。
python3 -m pip install uvicorn
-
インストールを確認します。
pyhton3 -m pip list
QRコードを作ってみる
python-qrcodeのドキュメントlincolnloop/python-qrcodeをGitHubで確認しておきます。
ドキュメントにあるサンプルコードをtest.pyとして試します。
import qrcode
img = qrcode.make("testです")
type(img) # qrcode.image.pil.PilImage
img.save("test.png")
これだけでQRコードが生成されました。しかし、気になる1行があります。
type(img) # qrcode.image.pil.PilImage
PillowImageでQRコードを生成したあとPNG画像としてPCに保存しています。
WebAPIから画像データをレスポンスにするためには、PNG画像のバイナリーを応答にする必要があります。そこで一度PilImageをPNG画像としてBytesIOに保存します。getvalue()でバッファーの内容を取得し、レスポンスにしました。書くと数行のことですがFastAPI側も関係するのでハマった箇所です。💦
試行錯誤中💦
QRコードをPNG画像としてバッファーに保存し、バイナリーデータを表示してみます。
import qrcode
from io import BytesIO
def qr_image_bytes(qr_word):
image = qrcode.make(qr_word)
vf = BytesIO()
image.save(vf, format="PNG")
return vf.getvalue()
print(qr_image_bytes(qr_word="こんばんは"))
なんとかうまくいきそうです。
ドキュメントのAdvanced Usageを参考にパラメーターを設定できるようにしました。
import qrcode
from io import BytesIO
def qr_image_bytes(qr_word,qr_version,error_correction,box_size,border):
qr=qrcode.QRCode(
version=qr_version,
error_correction=error_correction,
box_size=box_size,
border = border
)
qr.add_data(qr_word)
qr.make(fit=True)
image = qr.make_image(fill_color="black", back_color="white")
vf = BytesIO()
image.save(vf)
return vf.getvalue()
print(qr_image_bytes(qr_word="こんばんは",qr_version=1,error_correction=0,box_size=6,border=4))
誤り訂正能力のリテラルについては以下のようになっていました。
import qrcode
M=qrcode.ERROR_CORRECT_M
L=qrcode.ERROR_CORRECT_L
Q=qrcode.ERROR_CORRECT_Q
H=qrcode.ERROR_CORRECT_H
print (
f'7%:ERROR_CORRECT_L={L}\n',
f'15%:ERROR_CORRECT_M={M}\n',
f'25%:ERROR_CORRECT_Q={Q}\n',
f'30%:ERROR_CORRECT_H={H}'
)
# 7%:ERROR_CORRECT_L=1
# 15%:ERROR_CORRECT_M=0
# 25%:ERROR_CORRECT_Q=3
# 30%:ERROR_CORRECT_H=2
FastAPIとqrcodeを組み合わせる
FastAPIのドキュメントは素晴らしいのでオススメです。 チュートリアルからとりあえずHello Worldをやってみます。
ターミナルからUvicornを起動します。
uvicorn main:app --reload
uvicorn ファイル名:インスタンス名 --reload(再読み込み)
ブラウザからhttp://127.0.0.1:8000
にアクセスするとHello worldが返ってきます。
http://127.0.0.1:8000/docs
にアクセスしてみます。
Swagger UIのAPIドキュメントを自動生成してくれています。すごい。
サーバーはcontrol+cで停止できます。
準備は整ったのでクエリーパラメーターにqrcode関数の引数を設定する方針で組み合わせます。今回のポイントはレスポンスで画像を返すところでした。ドキュメンを読みながらいろいろ試行錯誤した結果、
- responseにメディアタイプを
media_type="image/png"
指定することで、PNG画像を返すことができました。 - Queryパラメーターを使用することで型のバリデーションの他に、デフォルト値、description、値の範囲バリデーションなどを設定できることがわかりました。
参考FastAPIドキュメント
- レスポンスを直接返す
- カスタムレスポンス - HTML、ストリーム、ファイル、その他のレスポンス
- クエリパラメータと文字列の検証
- Path Parameters and Numeric Validations
from fastapi import FastAPI,Query,Response
import qrcode
from io import BytesIO
app = FastAPI()
@app.get("/")
async def qr_image(
qr_word: str,
qr_version: int= Query(1,description="QRコードのバージョン。1〜40で設定。バージョン1の場合21x21マトリックス。デフォルト値1",ge=1,le=40),
error_correction: int = Query(0,description="誤り訂正能力 レベルL:7%=1,レベルM:15%=0(デフォルト),レベルQ:25%=3,レベルH:30%=2",ge=0,le=3),
box_size: int = Query(6,description="サイズのコントロール。1マトリックスあたりのピクセル数。デフォルト値6",ge=1,le=10),
border: int = Query(4,description="外枠の太さのコントロール。デフォルト値4はQRコード仕様の最小値。",ge=4,le=8)
):
qr = qrcode.QRCode(
version=qr_version,
error_correction=error_correction,
box_size=box_size,
border=border
)
qr.add_data(qr_word)
qr.make(fit=True)
image = qr.make_image(fill_color="black", back_color="white")
vf = BytesIO()
image.save(vf, format="PNG")
return Response(vf.getvalue(), media_type="image/png")
動作確認
クエリパラメーターを設定しないで、ルートにアクセスするとエラーが返ります。
http://127.0.0.1:8000/docs
にアクセスしGETを展開すると、descriptionやバリデーション付きの各パラメーターが現れます。
デフォルト値が設定されたパラメーターは必須項目ではありません。
Try it Outから進むと各パラメーターが設定できるようになります。
入力後ExecuteでQRコードの画像が表示されれば成功です。
Request URLをコピーしてブラウザのアドレス欄にペーストして実行します。
QRコードのPNG画像が表示されます。
まとめ
Pythonを利用してqrcodeとFastAPIを組み合わせることでQRコードを発行するAPIの作成ができました。これまでAPIを利用したことはありましたが、つくったことはありませんせした。ライブラリーを利用させてもらって、組み合わせるというのローコード的な作り方でしたが、とても勉強になりました。FastAPIのドキュメントは読みやすく試しやすいのでオススメです! 次回はつくったAPIをデプロイします。