Что такое REST API и его принципы?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое REST API и его принципы
REST (Representational State Transfer) — архитектурный стиль для построения масштабируемых веб-сервисов, которые используют HTTP протокол для операций над ресурсами.
Что такое REST API?
REST API — это интерфейс, который позволяет клиентам взаимодействовать с сервером через HTTP запросы для выполнения CRUD операций (Create, Read, Update, Delete) над ресурсами.
# Пример REST API на FastAPI
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class User(BaseModel):
id: int
name: str
email: str
# In-memory хранилище
users_db = {}
# CREATE
@app.post("/users")
def create_user(user: User):
users_db[user.id] = user
return {"message": "User created", "user": user}
# READ
@app.get("/users/{user_id}")
def get_user(user_id: int):
if user_id not in users_db:
return {"error": "User not found"}
return users_db[user_id]
# UPDATE
@app.put("/users/{user_id}")
def update_user(user_id: int, user: User):
users_db[user_id] = user
return {"message": "User updated", "user": user}
# DELETE
@app.delete("/users/{user_id}")
def delete_user(user_id: int):
if user_id in users_db:
del users_db[user_id]
return {"message": "User deleted"}
return {"error": "User not found"}
6 основных принципов REST
1. Client-Server архитектура
Клиент и сервер разделены и независимы друг от друга:
# Клиент (JavaScript)
fetch('/api/users/1')
.then(response => response.json())
.then(data => console.log(data));
# Сервер (Python)
@app.get("/users/{user_id}")
def get_user(user_id: int):
return {"id": user_id, "name": "John"}
# Клиент не знает, как сервер хранит данные
# Сервер не зависит от клиента
2. Statelessness (Безсостояние)
Каждый запрос содержит всю информацию, необходимую для его обработки. Сервер не хранит контекст клиента между запросами:
# Хорошо: Stateless
@app.get("/users/{user_id}")
def get_user(user_id: int, token: str = Header(None)):
# Проверяем token в каждом запросе
if not verify_token(token):
raise HTTPException(status_code=401)
return {"id": user_id, "name": "John"}
# Плохо: Stateful
sessions = {}
@app.post("/login")
def login(username: str, password: str):
# Сохраняем состояние сессии на сервере
session_id = str(uuid.uuid4())
sessions[session_id] = {"username": username, "logged_in": True}
return {"session_id": session_id}
@app.get("/profile")
def get_profile(session_id: str):
# Зависит от состояния, хранящегося на сервере
if session_id not in sessions:
raise HTTPException(status_code=401)
return {"user": sessions[session_id]["username"]}
3. Cacheable (Кэшируемость)
Ответы должны быть явно обозначены как кэшируемые или не кэшируемые:
from fastapi import Response
from datetime import datetime, timedelta
# Кэшируемый ответ
@app.get("/static-data")
def get_static_data():
return Response(
content="Static data",
headers={
"Cache-Control": "public, max-age=3600", # Кэш на 1 час
"ETag": '"123456"',
"Last-Modified": datetime.now().isoformat()
}
)
# Не кэшируемый ответ
@app.get("/dynamic-data")
def get_dynamic_data():
return Response(
content="Dynamic data",
headers={
"Cache-Control": "no-cache, no-store, must-revalidate",
"Pragma": "no-cache",
"Expires": "0"
}
)
4. Uniform Interface (Единообразный интерфейс)
Три компонента:
a) Идентификация ресурсов в запросе
# Правильно: ресурсы идентифицируются URI
GET /api/users/123 # Конкретный пользователь
GET /api/users # Все пользователи
GET /api/users/123/posts # Посты пользователя
# Неправильно: действия в URI
GET /api/getUser/123
GET /api/deleteUser/123
GET /api/updateUser/123
b) Манипуляция ресурсов через представления
# Правильно: используем HTTP методы для операций
POST /users # Создать
GET /users/123 # Прочитать
PUT /users/123 # Полное обновление
PATCH /users/123 # Частичное обновление
DELETE /users/123 # Удалить
# Пример на FastAPI
@app.post("/users") # Create
def create_user(user: User): ...
@app.get("/users/{id}") # Read
def read_user(id: int): ...
@app.put("/users/{id}") # Update (все поля)
def update_user(id: int, user: User): ...
@app.patch("/users/{id}") # Update (отдельные поля)
def partial_update(id: int, user: dict): ...
@app.delete("/users/{id}") # Delete
def delete_user(id: int): ...
c) Саморазъясняющиеся сообщения
from fastapi import status, Response
@app.post("/users", status_code=status.HTTP_201_CREATED)
def create_user(user: User):
# Response явно указывает, что создано новое
return Response(
content={"id": 1, "name": "John"},
status_code=201,
headers={"Content-Type": "application/json"}
)
@app.get("/users/{user_id}", response_model=User)
def get_user(user_id: int):
# Response явно указывает формат
return {"id": user_id, "name": "John"}
5. Layered System (Многоуровневая архитектура)
Клиент не может сказать, подключен ли он напрямую к конечному серверу:
# API Gateway layer
@app.get("/api/v1/users/{user_id}")
def get_user_v1(user_id: int):
# Может перенаправлять на разные сервисы
return requests.get(f"http://user-service:8001/users/{user_id}")
# Балансировка нагрузки
# Client -> Load Balancer -> Server 1, Server 2, Server 3
# Кэширование на промежуточном уровне
@app.get("/api/v1/users/{user_id}")
def get_user(user_id: int):
# Проверяем кэш
cached = redis.get(f"user:{user_id}")
if cached:
return json.loads(cached)
# Если нет в кэше, берём из БД
user = db.query(User).get(user_id)
redis.set(f"user:{user_id}", json.dumps(user))
return user
6. Code on Demand (Опционально)
Сервер может расширять функциональность клиента, отправляя исполняемый код:
# Редко используется, но возможно
@app.get("/api/script.js")
def get_script():
script = """
function validateEmail(email) {
return email.includes('@');
}
"""
return Response(content=script, media_type="application/javascript")
# На фронте
# <script src="/api/script.js"></script>
HTTP методы и их использование
from fastapi import FastAPI, status
app = FastAPI()
# GET - безопасен, идемпотентен, кэшируется
@app.get("/users")
def list_users():
"""Получить всех пользователей"""
return []
# POST - не идемпотентен, создает новые ресурсы
@app.post("/users", status_code=status.HTTP_201_CREATED)
def create_user(user: dict):
"""Создать нового пользователя"""
return {"id": 1, **user}
# PUT - идемпотентен, заменяет весь ресурс
@app.put("/users/{user_id}")
def replace_user(user_id: int, user: dict):
"""Полностью заменить пользователя"""
return {"id": user_id, **user}
# PATCH - не идемпотентен, обновляет части ресурса
@app.patch("/users/{user_id}")
def update_user(user_id: int, updates: dict):
"""Обновить часть пользователя"""
return {"id": user_id, "name": "Updated"}
# DELETE - идемпотентен
@app.delete("/users/{user_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_user(user_id: int):
"""Удалить пользователя"""
return None
# HEAD - как GET, но без body
@app.head("/users")
def head_users():
"""Проверить, существует ли ресурс"""
return {}
# OPTIONS - описать доступные методы
@app.options("/users")
def options_users():
"""Какие методы доступны для ресурса"""
return {"methods": ["GET", "POST", "PUT", "DELETE"]}
HTTP коды ответов
# 2xx - Успех
200 OK # Запрос успешен
201 Created # Ресурс создан
204 No Content # Успех, но нет контента
# 3xx - Перенаправление
301 Moved Permanently # Ресурс перемещен навсегда
304 Not Modified # Используй кэш
# 4xx - Ошибка клиента
400 Bad Request # Неверный запрос
401 Unauthorized # Нужна аутентификация
403 Forbidden # Нет прав доступа
404 Not Found # Ресурс не найден
409 Conflict # Конфликт (напр., дублирование)
# 5xx - Ошибка сервера
500 Internal Error # Ошибка на сервере
503 Service Unavailable # Сервис недоступен
Пример полного REST API
from fastapi import FastAPI, HTTPException, status
from typing import List
from pydantic import BaseModel
app = FastAPI()
class UserSchema(BaseModel):
id: int
name: str
email: str
users = {}
@app.get("/users", response_model=List[UserSchema])
def get_users():
return list(users.values())
@app.post("/users", status_code=status.HTTP_201_CREATED, response_model=UserSchema)
def create_user(user: UserSchema):
if user.id in users:
raise HTTPException(status_code=409, detail="User already exists")
users[user.id] = user
return user
@app.get("/users/{user_id}", response_model=UserSchema)
def get_user(user_id: int):
if user_id not in users:
raise HTTPException(status_code=404, detail="User not found")
return users[user_id]
@app.put("/users/{user_id}", response_model=UserSchema)
def update_user(user_id: int, user: UserSchema):
if user_id not in users:
raise HTTPException(status_code=404, detail="User not found")
users[user_id] = user
return user
@app.delete("/users/{user_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_user(user_id: int):
if user_id not in users:
raise HTTPException(status_code=404, detail="User not found")
del users[user_id]
return None
Резюме
REST API — это архитектурный стиль, который использует:
- HTTP методы (GET, POST, PUT, DELETE, PATCH) для операций
- URI для идентификации ресурсов
- HTTP коды для статусов
- Принципы: stateless, cacheable, layered, uniform interface
Это делает API предсказуемым, масштабируемым и простым для интеграции.