Hea programmeerimiskeele raamistik hõlbustab kvaliteetsete toodete kiiremat tootmist. Suurepärased raamistikud muudavad kogu arenduskogemuse isegi nauditavaks. FastAPI on uus Pythoni veebiraamistik, mida on võimas ja mõnus kasutada. Järgmised funktsioonid muudavad FastAPI proovimise väärtuks:
FastAPI taga olevate suurte ideede uurimiseks koostame rakenduse TODO, mis seab selle kasutajatele ülesandeloendid. Meie väike rakendus pakub järgmisi funktsioone:
Meie rakendusel on ainult kaks mudelit: kasutaja ja TODO. Pythoni andmebaasi tööriistakomplekti SQLAlchemy abil saame oma mudeleid väljendada järgmiselt:
class User(Base): __tablename__ = 'users' id = Column(Integer, primary_key=True, index=True) lname = Column(String) fname = Column(String) email = Column(String, unique=True, index=True) todos = relationship('TODO', back_populates='owner', cascade='all, delete-orphan') class TODO(Base): __tablename__ = 'todos' id = Column(Integer, primary_key=True, index=True) text = Column(String, index=True) completed = Column(Boolean, default=False) owner_id = Column(Integer, ForeignKey('users.id')) owner = relationship('User', back_populates='todos')
Kui meie mudelid on valmis, kirjutame SQLAlchemy konfiguratsioonifaili, et see teaks, kuidas andmebaasiga ühendust luua.
import os from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker SQLALCHEMY_DATABASE_URL = os.environ['SQLALCHEMY_DATABASE_URL'] engine = create_engine( SQLALCHEMY_DATABASE_URL) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) Base = declarative_base()
Suur osa mis tahes API-projektidest puudutab tavapäraseid asju nagu andmete valideerimine ja teisendamine. Käsitleme seda juba enne, kui hakkame päringute töötlejaid kirjutama. FastAPI abil väljendame oma sissetulevate / väljuvate andmete skeemi püdantiliste mudelite abil ja kasutame neid püdantilisi mudeleid vihje sisestamiseks ning andmete tasuta valideerimiseks ja teisendamiseks. Pange tähele, et need mudelid pole seotud meie andmebaasi töövoogudega ja määravad ainult meie REST-liidesest sisse ja välja voolavate andmete kuju. Püdantiliste mudelite kirjutamiseks mõelge läbi kõik võimalused, kuidas kasutaja ja TODO teave sisse ja välja voolavad.
Traditsiooniliselt registreerub uus kasutaja meie TODO teenusesse ja olemasolev kasutaja logib sisse. Mõlemad suhtlemised käsitlevad kasutajate teavet, kuid andmete kuju on erinev. Vajame registreerumisel kasutajatelt rohkem teavet ja sisselogimisel minimaalset teavet (ainult e-posti aadressi ja parooli). See tähendab, et nende kahe erineva kujuga Kasutajateabe väljendamiseks vajame kahte hüppelist mudelit.
Meie rakenduses TODO aga kasutame FastSAPis sisseehitatud OAuth2 tuge JSON-i veebimärkide (JWT) põhise sisselogimisvoo jaoks. Peame lihtsalt määrama UserCreate
siin skeem, et määrata andmed, mis voolavad meie registreerumise lõpp-punkti ja UserBase
skeem tagastatakse vastusena juhul, kui registreerumisprotsess õnnestub.
from pydantic import BaseModel from pydantic import EmailStr class UserBase(BaseModel): email: EmailStr class UserCreate(UserBase): lname: str fname: str password: str
Siin märkisime stringina perekonnanime, eesnime ja parooli, kuid seda saab pydantic abil veelgi karmistada piiratud stringid mis võimaldavad selliseid kontrolle nagu min pikkus, max pikkus ja regexes.
TODO-üksuste loomise ja loendamise toetamiseks määratleme järgmise skeemi:
class TODOCreate(BaseModel): text: str completed: bool
Olemasoleva TODO üksuse värskenduse toetamiseks määratleme veel ühe skeemi:
class TODOUpdate(TODOCreate): id: int
Sellega oleme kõigi andmevahetuste jaoks skeemid määratlenud. Pöörame nüüd tähelepanu päringukäitlejatele, kus neid skeeme kasutatakse kogu andmete teisendamise ja valideerimise raskeks tõstmiseks tasuta.
Esmalt lubame kasutajatel registreeruda, kuna kõikidele meie teenustele peab juurdepääs olema autentitud kasutaja. Oma esimese päringu töötleja kirjutame UserCreate
abil ja UserBase
eespool määratletud skeem.
@app.post('/api/users', response_model=schemas.User) def signup(user_data: schemas.UserCreate, db: Session = Depends(get_db)): '''add new user''' user = crud.get_user_by_email(db, user_data.email) if user: raise HTTPException(status_code=409, detail='Email already registered.') signedup_user = crud.create_user(db, user_data) return signedup_user
Selles lühikeses koodijupis toimub palju. Oleme kasutanud dekoraatorit HTTP-verbi, URI ja edukate vastuste skeemi määramiseks. Tagamaks, et kasutaja on esitanud õiged andmed, sisestasime päringu kehale vihje varem määratletud UserCreate
skeem. Meetod määratleb veel ühe parameetri andmebaasi käepideme saamiseks - see on sõltuvuse süstimine ja seda käsitletakse edaspidi selles õpetuses.
Soovime oma rakendusse järgmisi turvaelemente:
Parooli räsimiseks saame kasutada Passlibi. Määratleme funktsioonid, mis töötlevad parooli räsimist ja kontrollivad parooli õigsust.
from passlib.context import CryptContext pwd_context = CryptContext(schemes=['bcrypt'], deprecated='auto') def verify_password(plain_password, hashed_password): return pwd_context.verify(plain_password, hashed_password) def get_password_hash(password): return pwd_context.hash(password) def authenticate_user(db, email: str, password: str): user = crud.get_user_by_email(db, email) if not user: return False if not verify_password(password, user.hashed_password): return False return user
JWT-põhise autentimise lubamiseks peame looma nii JWT-d kui ka dekodeerima, et saada kasutaja mandaadid. Selle funktsiooni pakkumiseks määratleme järgmised funktsioonid.
# install PyJWT import jwt from fastapi.security import OAuth2PasswordBearer SECRET_KEY = os.environ['SECRET_KEY'] ALGORITHM = os.environ['ALGORITHM'] def create_access_token(*, data: dict, expires_delta: timedelta = None): to_encode = data.copy() if expires_delta: expire = datetime.utcnow() + expires_delta else: expire = datetime.utcnow() + timedelta(minutes=15) to_encode.update({'exp': expire}) encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) return encoded_jwt def decode_access_token(db, token): credentials_exception = HTTPException( status_code=HTTP_401_UNAUTHORIZED, detail='Could not validate credentials', headers={'WWW-Authenticate': 'Bearer'}, ) try: payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) email: str = payload.get('sub') if email is None: raise credentials_exception token_data = schemas.TokenData(email=email) except PyJWTError: raise credentials_exception user = crud.get_user_by_email(db, email=token_data.email) if user is None: raise credentials_exception return user
Nüüd määratleme sisselogimise lõpp-punkti ja juurutame OAuth2 paroolivoo. See lõpp-punkt saab meili ja parooli. Kontrollime mandaate andmebaasi põhjal ja edukuse korral väljastame kasutajale JSON-i veebimärgi.
Volituste saamiseks kasutame OAuth2PasswordRequestForm
, mis on osa FastAPI turvautiliitidest.
@app.post('/api/token', response_model=schemas.Token) def login_for_access_token(db: Session = Depends(get_db), form_data: OAuth2PasswordRequestForm = Depends()): '''generate access token for valid credentials''' user = authenticate_user(db, form_data.username, form_data.password) if not user: raise HTTPException( status_code=HTTP_401_UNAUTHORIZED, detail='Incorrect email or password', headers={'WWW-Authenticate': 'Bearer'}, ) access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) access_token = create_access_token(data={'sub': user.email}, expires_delta=access_token_expires) return {'access_token': access_token, 'token_type': 'bearer'}
Oleme seadistanud sisselogimise lõpp-punkti, mis pakub kasutajale eduka sisselogimise korral JWT-d. Kasutaja saab selle loa kohalikku salvestusruumi salvestada ja näidata meie tagaküljel autoriseerimise päisena. Lõpp-punktid, mis ootavad juurdepääsu ainult sisseloginud kasutajatelt, saavad lindi dekodeerida ja teada saada, kes on taotleja. Selline töö pole seotud kindla lõpp-punktiga, pigem on see kõigi kaitstud lõpp-punktides kasutatav jagatud loogika. Parim on seadistada märkide dekodeerimise loogika sõltuvuseks, mida saab kasutada mis tahes taotluste töötlejas.
FastAPI-speakes sõltuksid meie teeoperatsioonifunktsioonid (päringukäitlejad) get_current_user
-st. get_current_user
sõltuvusel peab olema ühendus andmebaasiga ja haakuma FastAPI-ga OAuth2PasswordBearer
loogika märgi saamiseks. Lahendame selle probleemi tehes get_current_user
sõltuvad muudest funktsioonidest. Nii saame määratleda sõltuvusahelad, mis on väga võimas mõiste.
def get_db(): '''provide db session to path operation functions''' try: db = SessionLocal() yield db finally: db.close() def get_current_user(db: Session = Depends(get_db), token: str = Depends(oauth2_scheme)): return decode_access_token(db, token) @app.get('/api/me', response_model=schemas.User) def read_logged_in_user(current_user: models.User = Depends(get_current_user)): '''return user settings for current user''' return current_user
Enne kui kirjutame TODO loomise, lugemise, värskendamise, kustutamise (CRUD) teeoperatsioonifunktsioonid, määratleme järgmised abifunktsioonid db-i tegeliku CRUD-i teostamiseks.
def create_todo(db: Session, current_user: models.User, todo_data: schemas.TODOCreate): todo = models.TODO(text=todo_data.text, completed=todo_data.completed) todo.owner = current_user db.add(todo) db.commit() db.refresh(todo) return todo def update_todo(db: Session, todo_data: schemas.TODOUpdate): todo = db.query(models.TODO).filter(models.TODO.id == id).first() todo.text = todo_data.text todo.completed = todo.completed db.commit() db.refresh(todo) return todo def delete_todo(db: Session, id: int): todo = db.query(models.TODO).filter(models.TODO.id == id).first() db.delete(todo) db.commit() def get_user_todos(db: Session, userid: int): return db.query(models.TODO).filter(models.TODO.owner_id == userid).all()
Neid db-taseme funktsioone kasutatakse järgmistes REST-lõpp-punktides:
@app.get('/api/mytodos', response_model=List[schemas.TODO]) def get_own_todos(current_user: models.User = Depends(get_current_user), db: Session = Depends(get_db)): '''return a list of TODOs owned by current user''' todos = crud.get_user_todos(db, current_user.id) return todos @app.post('/api/todos', response_model=schemas.TODO) def add_a_todo(todo_data: schemas.TODOCreate, current_user: models.User = Depends(get_current_user), db: Session = Depends(get_db)): '''add a TODO''' todo = crud.create_meal(db, current_user, meal_data) return todo @app.put('/api/todos/{todo_id}', response_model=schemas.TODO) def update_a_todo(todo_id: int, todo_data: schemas.TODOUpdate, current_user: models.User = Depends(get_current_user), db: Session = Depends(get_db)): '''update and return TODO for given id''' todo = crud.get_todo(db, todo_id) updated_todo = crud.update_todo(db, todo_id, todo_data) return updated_todo @app.delete('/api/todos/{todo_id}') def delete_a_meal(todo_id: int, current_user: models.User = Depends(get_current_user), db: Session = Depends(get_db)): '''delete TODO of given id''' crud.delete_meal(db, todo_id) return {'detail': 'TODO Deleted'}
Kirjutame mõned testid meie TODO API-le. FastAPI pakub TestClient
klass, mis põhineb populaarsel Requests'i teegil, ja saame teste teha Pytestiga.
Veendumaks, et ainult sisselogitud kasutajad saavad TODO-d luua, võime kirjutada umbes nii:
from starlette.testclient import TestClient from .main import app client = TestClient(app) def test_unauthenticated_user_cant_create_todos(): todo=dict(text='run a mile', completed=False) response = client.post('/api/todos', data=todo) assert response.status_code == 401
Järgmine test kontrollib meie sisselogimise lõpp-punkti ja genereerib JWT, kui sellel on kehtivad sisselogimismandaadid.
def test_user_can_obtain_auth_token(): response = client.post('/api/token', data=good_credentials) assert response.status_code == 200 assert 'access_token' in response.json() assert 'token_type' in response.json()
Oleme FastAPI abil rakendanud väga lihtsa TODO rakenduse. Nüüdseks olete näinud REST-liidese kaudu saabuvate ja väljaminevate andmete kuju määratlemisel hästi kasutatavat tüüpi vihjeid. Määratleme skeemid ühes kohas ja jätame andmete valideerimise ja teisendamise rakendamiseks FastAPI-le. Teine tähelepanuväärne omadus on sõltuvuse süstimine. Kasutasime seda kontseptsiooni andmebaasiühenduse hankimise jagatud loogika pakkimiseks, JWT dekodeerimiseks praegu sisselogitud kasutaja saamiseks ning lihtsa OAuth2 parooli ja kandjaga juurutamiseks. Nägime ka seda, kuidas sõltuvusi saab kokku aheldada.
Saame seda kontseptsiooni hõlpsalt rakendada selliste funktsioonide lisamiseks nagu rollipõhine juurdepääs. Pealegi kirjutame lühikese ja võimsa koodi, ilma et õpiksime raamistiku iseärasusi. Lihtsamalt öeldes on FastAPI kogum võimsatest tööriistadest, mida te ei pea õppima, sest need on lihtsalt kaasaegsed Python. Lõbutse hästi.
FastAPI on Pythoni raamistik ja tööriistakomplekt, mis võimaldab arendajatel kasutada REST-liidest rakenduste juurutamiseks sagedamini kasutatavate funktsioonide kutsumiseks. Sellele pääseb REST API kaudu juurde, et helistada rakenduse ühistele ehitusplokkidele. Selles näites kasutab autor FastAPI-d kontode loomiseks, sisselogimiseks ja autentimiseks.
Nagu kõik REST-liidesed, kutsutakse ka FastAPI-d teie koodilt. See pakub selliseid funktsioone nagu sisestatud andmete tüübi vihje, sõltuvuse sisestamine ja autentimine, et te ei peaks ise funktsioone kirjutama.
Kuigi avatud lähtekoodiga raamistik on FastAPI täielikult valmis tootmiseks, suurepärase dokumentatsiooni, toe ja hõlpsasti kasutatava liidesega. Seda saab kasutada sama kiirete rakenduste loomiseks ja käitamiseks kui teistes skriptikeeltes kirjutatud.
terminilehe mall soetamiseks
Võib juhtuda, et see liides on täpselt määratletud ja aluseks olev kood on optimeeritud. Dokumentatsioonis väidetakse, et FastAPI toimib sama hästi kui Node.js ja Go.