FastAPIで作るWebアプリ - validation

今回はRequest　パラメータのバリデーションを取り上げます。パスパラメータとクエリパラメータです。Swagger UIとともに、FastAPIでの開発効率を上げてくれるものです。

【過去記事】
[Python Asyncio入門](https://qiita.com/sand/items/0e445a13d81d20ea33c3)
[Aiohttpで作るSimple Web Server - Qiita](https://qiita.com/sand/items/971c851598ff59c1c905)
[Starletteで作る Simple Web Server - QIita](https://qiita.com/sand/items/dd622ce4f9249399974c)
[FastAPIで作るWebアプリ - 基本](https://qiita.com/sand/items/bba0913fe4504c612b3a)
[FastAPIで作るWebアプリ - validation](https://qiita.com/sand/items/85068532388826258df4)
[FastAPIで作るWebアプリ - Body validation](https://qiita.com/sand/items/470b33d7539713432090)
[FastAPIで作るWebアプリ - Form validation](https://qiita.com/sand/items/207f306bdacdc0fb9571)

[FastAPI 公式サイト](https://fastapi.tiangolo.com/ja/)



# 1.パスパラメータ

## 1-1.型チェック

前回と同じプログラムを使います。

```python:path.py
from fastapi import FastAPI

app = FastAPI()

@app.get("/items/{item_id}")
async def read_item(item_id : int):
    return {"item_id": item_id}
```

起動します

```
uvicorn path:app --reload
```

前回はもっぱらSwagger UIからRequestを投げましたが、今回はサーバ側のエラーチェックを確認するためcurlコマンドを使います。

```
curl http://localhost:8000/items/123 
```

サーバ側のステータス(200 OK)も確認して、クラシアン側に以下のコンテンツが返ってくることも確認します。

```
{"item_id":123}
```

今度はエラーデータをRequestします。

```
curl http://localhost:8000/items/f00 
```

サーバ側のステータス(422 Unprocessable Entity)はエラーとなり、クライアント側にも以下のエラーが返ってきます。型を正しくチェックしているのがわかります。


```
{"detail":[{"loc":["path","item_id"],"msg":"value is not a valid integer","type":"type_error.integer"}]}
```

## 1-2.Enumによる値の制限

Enumを使ってパスパラメータの値を制限することができます。

```python:pathenum.py
from enum import Enum
from fastapi import FastAPI

app = FastAPI()

Class Color(str,Enum)
    RED = 'red'
    GREEN = 'green'
    BLUE = 'blue'

@app.get("/items/{color}/{item_id}")
async def read_item(color : Color, item_id : int):
    return {"color": color, "item_id": item_id}
```


ここでEnum classであるColorは親としてstrが必要です。
[String-based enum in Python](https://stackoverflow.com/questions/58608361/string-based-enum-in-python)


正しいRequestを投げます

```
curl http://localhost:8000/items/red/123
```

サーバ側は200 okで、クライアント側は以下のコンテンツを受け取ります。

```
{"color":"red","item_id":123}
```

それでは正しくないRequestを投げます。'black'はEnumには含まれていません。

```
curl http://localhost:8000/items/black/123
```

サーバ側は422 Unprocessable Entityとなり、クライアント側は以下のエラーを受け取ります。

```
{"detail":[{"loc":["path","color"],"msg":"value is not a valid enumeration member; permitted: 'red', 'green', 'blue'","type":"type_error.enum","ctx":{"enum_values":["red","green","blue"]}}]}
```

ここまでで、如何にvalidationが、Pythonのtype hintの記述で、自然に行われるかがわかります。もっと多くのエラーチェックがありますが、次に進んでいきましょう。


# 2.クエリパラメータ

以下のようなプログラムを考えます。

```python:query.py
from typing import Optional

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: Optional[str] = Query(None, max_length=5)):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results
```


**Query()関数**でより進んだValidationを行うことができます。第一引数にdefault値をとり、第二引数以降にValidationを記述します。Query(None, min_length=3) はデフォルト値がNoneで3文字以上の文字列の値が必要です。**デフォルト値を指定せず、値の指定が必須の場合はQuery(..., min_length=3)と書きます。**

**(補足2022/07/22）**
(...)はPythonの値で**Ellipsis**と呼ばれています。
**PydanticとFastAPI において、Ellipsisは値の指定が必須であることを表現するために使われています。**
[Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/)

それでは正常なRequestを発行してみましょう

```
curl http://localhost:8000/items/?q=foo
```

サーバ側に200 okが表示され、クライアント側には以下の値が返ります。

```
{"items":[{"item_id":"Foo"},{"item_id":"Bar"}],"q":"foo"}
```

それでは文字列に長さが５を超えるものを指定してみましょう。

```
curl http://localhost:8000/items/?q=foofoo
```

サーバ側に422 Unprocessable Entityが表示され、クライアント側に以下のエラーが表示されます。

```
{"detail":[{"loc":["query","q"],"msg":"ensure this value has at most 5 characters","type":"value_error.any_str.max_length","ctx":{"limit_value":5}}]}
```

パスパラメータとクエリパラメータのValidationについて見てきましたが、とても簡単です。またここでは十分に取り上げられませんでしたが、とても豊富です。FastAPIの開発効率が良いのも十分理解できます。

Request Bodyについては次回取り上げるつもりです。

今回は以上です。