WebAPI作成に挑戦(1)「QRコードを発行するAPIをつくってみる」

概要

QRコードを発行するWebAPIをデプロイし、Power Automateのカスタムコネクタから接続します。全3回を予定しています。

  1. 「QRコードを発行するAPIをつくってみる」
  2. 「WebAPIとしてデプロイ」
  3. 「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インストール

  1. Visual Studio Codeから新規でフォルダーを開きます。フォルダー名を「QRAPI」にしました。今回はMacを使用していますがWindowsの場合はpyhton3をpyにして実行します。(公式版のインストールでパスを通してある場合)

  2. Pythonのバージョン確認しておきます。python3 --version

  3. venvを作成します。今回は環境名もvenvにしました。python3 -m venv 仮想環境名

  4. venvを実行します。`. venv/bin/activate``

  5. pillowをインストールします。python3 -m pip install pillow

  6. qrcodeをインストールします。python3 -m pip install qrcode

  7. インストールを確認します。pyhton3 -m pip list

FastAPIとUvicornのインストール

  1. FastAPIのインストール

    FastAPI は、Pythonの標準である型ヒントに基づいてPython 3.6 以降でAPI を構築するための、モダンで、高速(高パフォーマンス)な、Web フレームワークです。

    python3 -m pip install fastapi

  2. Uvicornのインストール

    Uvicorn is an ASGI (Asynchronous Server Gateway Interface) web server implementation for Python.

    とあるように非同期対応のPython用Webサーバです。開発時の動作確認に使用します。 python3 -m pip install uvicorn

  3. インストールを確認します。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ドキュメント

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コードの画像が表示されれば成功です。

movie

Request URLをコピーしてブラウザのアドレス欄にペーストして実行します。

QRコードのPNG画像が表示されます。

まとめ

Pythonを利用してqrcodeとFastAPIを組み合わせることでQRコードを発行するAPIの作成ができました。これまでAPIを利用したことはありましたが、つくったことはありませんせした。ライブラリーを利用させてもらって、組み合わせるというのローコード的な作り方でしたが、とても勉強になりました。FastAPIのドキュメントは読みやすく試しやすいのでオススメです! 次回はつくったAPIをデプロイします。