Перейти к содержимому

Экзаменационные практики


Практические задания: FastAPI (реализация кода)

Задание 1. Создание базового приложения

Напишите минимальное FastAPI-приложение с эндпойнтом GET /, который возвращает JSON {"message": "Hello, World!"}.

# Допишите код:
from fastapi import FastAPI
app = FastAPI()
# Ваш код здесь

Задание 2. Pydantic-модель для валидации

Создайте Pydantic-модель Product со следующими полями:

  • name (str, обязательное, минимум 1 символ)
  • price (float, обязательное, больше 0)
  • quantity (int, по умолчанию 0, >= 0)
  • category (str, опциональное)
from pydantic import BaseModel, Field
from typing import Optional
# Ваш код здесь
class Product(BaseModel):
pass

Задание 3. POST-эндпойнт для создания ресурса

Реализуйте эндпойнт POST /products/, который:

  • Принимает JSON с данными продукта (модель ProductCreate)
  • Генерирует UUID для нового продукта
  • Сохраняет в словарь DB
  • Возвращает созданный продукт с кодом 201 Created
from fastapi import FastAPI, status
from uuid import uuid4, UUID
from pydantic import BaseModel
app = FastAPI()
DB: dict = {}
class ProductCreate(BaseModel):
name: str
price: float
class Product(ProductCreate):
id: UUID
# Ваш код здесь
@app.post("/products/", ...)
def create_product(...):
pass

Задание 4. GET с параметрами запроса (Query)

Реализуйте эндпойнт GET /products/, который:

  • Поддерживает параметры min_price, max_price (фильтрация по цене)
  • Поддерживает параметр limit (по умолчанию 10, от 1 до 100)
  • Возвращает отфильтрованный список продуктов
from fastapi import FastAPI, Query
from typing import Optional, List
# Ваш код здесь
@app.get("/products/", response_model=List[Product])
def list_products(
# Опишите параметры
):
pass

Задание 5. GET по ID с обработкой 404

Реализуйте эндпойнт GET /products/{product_id}, который:

  • Принимает product_id типа UUID
  • Возвращает продукт, если найден
  • Возвращает 404 Not Found с сообщением, если продукт не найден
from fastapi import FastAPI, HTTPException, Path
from uuid import UUID
# Ваш код здесь
@app.get("/products/{product_id}")
def get_product(product_id: UUID):
pass

Задание 6. PATCH для частичного обновления

Реализуйте эндпойнт PATCH /products/{product_id}, который:

  • Принимает модель ProductUpdate (все поля опциональные)
  • Обновляет только переданные поля
  • Возвращает обновлённый продукт
from pydantic import BaseModel
from typing import Optional
class ProductUpdate(BaseModel):
name: Optional[str] = None
price: Optional[float] = None
# Ваш код здесь
@app.patch("/products/{product_id}")
def update_product(product_id: UUID, payload: ProductUpdate):
pass

Задание 7. DELETE с кодом 204

Реализуйте эндпойнт DELETE /products/{product_id}, который:

  • Удаляет продукт по ID
  • Возвращает код 204 No Content при успехе
  • Возвращает 404 Not Found, если продукт не найден
from fastapi import status
# Ваш код здесь
@app.delete("/products/{product_id}", status_code=...)
def delete_product(product_id: UUID):
pass

Задание 8. Dependency Injection для работы с БД

Реализуйте функцию-зависимость get_db(), которая:

  • Создаёт сессию базы данных
  • Возвращает её через yield
  • Закрывает сессию в блоке finally
from fastapi import Depends
from sqlalchemy.orm import Session
def get_db():
# Ваш код здесь
pass
@app.get("/items/")
def get_items(db: Session = Depends(get_db)):
pass

Задание 9. JWT-аутентификация

Реализуйте функцию create_access_token(), которая:

  • Принимает user_id и опциональный expires_delta
  • Создаёт JWT с полями sub, exp, iat
  • Возвращает закодированный токен
import jwt
from datetime import datetime, timedelta
SECRET_KEY = "your-secret-key"
ALGORITHM = "HS256"
def create_access_token(user_id: str, expires_delta: timedelta = None) -> str:
# Ваш код здесь
pass

Задание 10. Защита эндпойнта с JWT

Реализуйте зависимость verify_token(), которая:

  • Извлекает токен из заголовка Authorization: Bearer <token>
  • Декодирует и валидирует JWT
  • Возвращает 401 Unauthorized при невалидном/просроченном токене
  • Возвращает payload токена при успехе
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
security = HTTPBearer()
def verify_token(credentials: HTTPAuthorizationCredentials = Depends(security)):
# Ваш код здесь
pass
@app.get("/protected/")
def protected_route(payload: dict = Depends(verify_token)):
return {"user_id": payload["sub"]}

Задание 11. Хеширование паролей

Реализуйте функции hash_password() и verify_password() с использованием bcrypt:

import bcrypt
def hash_password(password: str) -> str:
# Ваш код здесь
pass
def verify_password(plain_password: str, hashed_password: str) -> bool:
# Ваш код здесь
pass

Задание 12. RBAC (Role-Based Access Control)

Реализуйте декоратор/зависимость require_role(), который:

  • Проверяет роль пользователя из JWT
  • Возвращает 403 Forbidden, если роль не соответствует требуемой
from enum import Enum
class Role(str, Enum):
ADMIN = "admin"
USER = "user"
def require_role(required_role: Role):
def checker(payload: dict = Depends(verify_token)):
# Ваш код здесь
pass
return checker
@app.delete("/admin/users/{user_id}")
def delete_user(user_id: int, user = Depends(require_role(Role.ADMIN))):
pass

Задание 13. Обработка ошибок валидации

Создайте кастомный обработчик исключений для RequestValidationError, который возвращает ошибки в формате:

{
"error": {
"code": "VALIDATION_ERROR",
"message": "Ошибка валидации данных",
"details": [...]
}
}
from fastapi import FastAPI
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
app = FastAPI()
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request, exc):
# Ваш код здесь
pass

Задание 14. Rate Limiting

Реализуйте простой Rate Limiter, который ограничивает количество запросов с одного IP до 10 в минуту:

from fastapi import Request, HTTPException
from collections import defaultdict
from datetime import datetime, timedelta
# Хранилище запросов: {ip: [timestamp1, timestamp2, ...]}
request_counts = defaultdict(list)
async def rate_limit_middleware(request: Request, call_next):
# Ваш код здесь
pass