FastAPI
こっちのほうが良さそう ナウくて良い
ASGIはWSGIの後継で非同期処理が可能!
型アノテーションのライブラリらしい
いい感じなので先にQiitaのチュートリアルやる
こっからやる
FastAPIすげぇ
大まかにわかった感じ
これ好き
やるお
これ将来的にやったほうがよい
Enum
を用いるとクエリパラメータで使える値を限定できる。
また、{file_path:path}
のようにデコレータを設定して、引数にfile_pathを設定することで、src/js/index.js
のようなパス変数をURLで使用できる
request-body.
Pydanticを用いることでPostデータも自動でバリデーションすることができる
うれしい
PathやQueryはパスパラメータ クエリパラメータに制約を追加できる
BodyでPostされたデータをバリデーションできる
Pydanticはネストできる
Swaggerに反映できるスキーマを設定できる
from typing import Optional
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
tax: Optional[float] = None
class Config:
schema_extra = {
"example": {
"name": "Foo",
"tax": 3.2,
}
}
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
Cookie
Headerの受け取り方
なんか読み落としてたっぽいけど
user_agent: Optional[str] = Header(None)
って書くとHeaderからuser-agentを取り出すことができる?
(変数名が一致する物を取り出している?)
response_model
で返すJSONのスキーマを設定できる
BaseModelで絞ることが可能
from typing import Optional
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr
app = FastAPI()
class UserIn(BaseModel):
username: str
password: str
email: EmailStr
full_name: Optional[str] = None
class UserOut(BaseModel):
username: str
email: EmailStr
full_name: Optional[str] = None
@app.post("/user/", response_model=UserOut)
async def create_user(user: UserIn):
return user
Userに関するものを扱うときに
- postで入力される
- DBに入れる(ハッシュ)
- 返す(パスワードを含まない)
をするときにそれぞれClassでやるとこわいので
Base
を作ってそれを継承しよう
from typing import Optional
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr
app = FastAPI()
class UserBase(BaseModel):
username: str
email: EmailStr
full_name: Optional[str] = None
class UserIn(UserBase):
password: str
class UserOut(UserBase):
pass
class UserInDB(UserBase):
hashed_password: str
def fake_password_hasher(raw_password: str):
return "supersecret" + raw_password
def fake_save_user(user_in: UserIn):
hashed_password = fake_password_hasher(user_in.password)
user_in_db = UserInDB(**user_in.dict(), hashed_password=hashed_password)
print("User saved! ..not really")
return user_in_db
@app.post("/user/", response_model=UserOut)
async def create_user(user_in: UserIn):
user_saved = fake_save_user(user_in)
return user_saved
status codeの設定
Formを受け取ることができる()
from fastapi import FastAPI, Form
app = FastAPI()
@app.post("/login/")
async def login(username: str = Form(...), password: str = Form(...)):
return {"username": username}
ファイルをAPIで受け取れる
UploadFile
でファイル形式でいい感じにプロパティを持ったまま受け取れる
async file.read()などを実行して読み取れる
apiのエラーについて
404などはraise HTTPException
で表現できる。
またカスタムで色々と設定できる
タグを設定できる
こうするとSwaggerなどが見やすいようにスキーマが設定される
DocStringや summaryなどを設定することでスキーマが読みやすくなる
情報をまるっと変えるのはPUT
一部のみ書き換えるならPATCH
が良い
そのためのメンバ関数も用意してある
依存性注入。
外部から変更を受け付けるように設計することによって
- テストしやすい
- 変更しやすい
- スタブなどを作成しやすい
- 動作を変更しやすい
などに作成できる。
外部から操作できるように作ることが大事であり、FastAPIはそれができるように(外部から操作を受け付けるように)作られている
認証: Authentication
相手が誰
かを判別する・識別すること
相手がわかったからと言ってすべてのリソースにアクセスを当然許すわけではない
認可: Authorization
リソースに権限を与えること
「認証」されたからアカウントへの「認可」・ツイートの「認可」を与える
依存性注入によって、かんたんに今のユーザを取り出せる
class User(BaseModel):
username: str
email: Optional[str] = None
disabled: Optional[bool] = None
def fake_decode_token(token):
return User(
username=token + 'fakedecoded',
email='example@com',
)
async def get_current_user(token: str = Depends(oauth2_scheme)):
user = fake_decode_token(token)
return user
@app.get('/users/me')
async def read_users_me(current_user: User = Depends(get_current_user)):
return current_user
OAuth2とPasswordの認証チュートリアル
bearerはOAuth2の一つで、ユーザ名とパスワードで認証し、アクセストークンでユーザかを判定するっぽい(ここ実際に実装して勉強がいる)
oauth2_scheme = OAuth2PasswordBearer(tokenUrl='token')
で、tokenのパスを指定してSwaggerがtokenパスでログインできるようになっている
ユーザにはトークンを返してあげる
そして、ユーザからアクセスされたトークンが正しければOK
JWTを使って認証のトークンを作成する。
POSTでusername
とpassword
を受け取る。(この名前にOpenAPIで規定されている)。
そして、passwordをサーバーでハッシュして、返却データのヘッダを作成する。
これらのヘッダ+データ+シグネチャをJWTとしてBase64したものをトークンとして返す。
ユーザはこのトークンをブラウザにアクセスするときに渡すのでそれをサーバーが確認する。
このトークンとJWTでデコードしてそれがあっていればOK.
また、サーバからJWTで返されるデータはペイロードとしてブラウザが持ち続ける...?(ここ毎回やらないといけないのかわかってないので調べる)
明日はここから
originとは
- プロトコル
- ドメイン
- ポート
の組み合わせでどれか一つでも違うとCORSに対処する必要がある
SQLとの連携。
ORMは、テーブル(複数形)と 言語におけるクラスを一致させる。各行がインスタンスに相当する。
スキーマ自体はpydanticで作成する。
これによって、リクエスト時やDocsで型を付けて、間違えたリクエストはFastAPI側で破棄できる。
実際にテーブルを操作するときは、model
をSQLAlchemeyで作成して、SQLAlchemy側で操作する。
CRUD関数を作成して、これらのmodel
とschema(pydantic)
の間を取り持っている。
ファイルの分割例。
Pythonのモジュールも含めてすごく丁寧に説明されている(すごい)。
routersを作って、特定のクラスごとに書いても良い。
ただ、これやるとデータベース周りとどうしようかな...
schemas
とmodels
から読み込めばいいんかな
Routerを使うとあるクエリパス以下ごとに作れるので嬉しい
testを書こう!
デバッグのやり方
これで一通りおわったので早速なにか作る