Что обозначают 400-ые коды ответов http?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
HTTP 400-ые коды (Client Error)
400-ые коды обозначают ошибки со стороны клиента — неправильный запрос, неверные данные, отсутствие прав и т.д.
Категория 4xx: Client Error
Ошибки клиента (400-499)
├─ 400 Bad Request — неправильный формат запроса
├─ 401 Unauthorized — требуется аутентификация
├─ 403 Forbidden — нет прав доступа
├─ 404 Not Found — ресурс не существует
├─ 405 Method Not Allowed — неправильный HTTP метод
├─ 409 Conflict — конфликт (например, дублирование)
├─ 410 Gone — ресурс был удален
├─ 413 Payload Too Large — файл слишком большой
├─ 415 Unsupported Media Type — неподдерживаемый формат
├─ 422 Unprocessable Entity — синтаксически верно, но не может быть обработано
└─ 429 Too Many Requests — слишком много запросов (rate limiting)
Самые частые 4xx коды
400 Bad Request
Клиент отправил некорректный запрос.
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, ValidationError
app = FastAPI()
class UserCreate(BaseModel):
email: str
age: int
@app.post("/users")
async def create_user(user: UserCreate):
# Pydantic автоматически возвращает 422 при ошибке валидации
# Или можем вернуть явно 400
if len(user.email) < 5:
raise HTTPException(
status_code=400,
detail="Email должен быть длиной хотя бы 5 символов"
)
return {"email": user.email}
# Примеры неправильных запросов:
# POST /users {"email": "a", "age": "not a number"} -> 422
# POST /users {"email": "a"} -> 422
# POST /users {} -> 422
401 Unauthorized
Аутентификация обязательна, но не предоставлена или невалидна.
from fastapi import Depends, HTTPException
from fastapi.security import HTTPBearer, HTTPAuthCredentials
security = HTTPBearer()
@app.get("/protected")
async def protected_resource(credentials: HTTPAuthCredentials = Depends(security)):
# Проверяем токен
if not verify_token(credentials.credentials):
raise HTTPException(
status_code=401,
detail="Invalid or expired token",
headers={"WWW-Authenticate": "Bearer"} # Подсказка клиенту
)
return {"data": "secret"}
# Примеры неправильных запросов:
# GET /protected # Без Authorization header -> 401
# GET /protected -H "Authorization: Bearer invalid_token" -> 401
# Правильный запрос:
# GET /protected -H "Authorization: Bearer valid_token" -> 200
403 Forbidden
Клиент аутентифицирован, но не имеет прав доступа.
from fastapi import Depends
from sqlalchemy.orm import Session
@app.delete("/users/{user_id}")
async def delete_user(
user_id: int,
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
user_to_delete = db.query(User).get(user_id)
# Проверяем права: только admin или сам пользователь
if current_user.id != user_id and current_user.role != 'admin':
raise HTTPException(
status_code=403,
detail="You don't have permission to delete this user"
)
db.delete(user_to_delete)
db.commit()
return {"deleted": True}
# Примеры:
# DELETE /users/123 (как user_456) -> 403 Forbidden
# DELETE /users/456 (как user_456) -> 200 OK
# DELETE /users/789 (как admin) -> 200 OK
404 Not Found
Ресурс не существует.
@app.get("/articles/{article_id}")
async def get_article(article_id: int, db: Session = Depends(get_db)):
article = db.query(Article).get(article_id)
if not article:
raise HTTPException(
status_code=404,
detail=f"Article with id {article_id} not found"
)
return article
# Примеры:
# GET /articles/999 (не существует) -> 404
# GET /articles/1 (существует) -> 200
405 Method Not Allowed
HTTP метод не разрешен для этого ресурса.
from fastapi import APIRouter
router = APIRouter()
# Определяем какие методы разрешены
@router.get("/articles") # Только GET
async def list_articles():
return [{"id": 1, "title": "Article 1"}]
# DELETE не определен для этого endpoint-a
# Примеры:
# GET /articles -> 200
# POST /articles -> 405 Method Not Allowed
# DELETE /articles -> 405 Method Not Allowed
409 Conflict
Конфликт — например, попытка создать ресурс с дублирующимся ID.
@app.post("/emails")
async def register_email(email: str, db: Session = Depends(get_db)):
# Проверяем, существует ли уже такой email
existing = db.query(User).filter(User.email == email).first()
if existing:
raise HTTPException(
status_code=409,
detail=f"Email {email} is already registered"
)
new_user = User(email=email)
db.add(new_user)
db.commit()
return new_user
# Примеры:
# POST /emails {"email": "new@example.com"} -> 201
# POST /emails {"email": "new@example.com"} (второй раз) -> 409 Conflict
410 Gone
Ресурс был удален постоянно.
@app.get("/deprecated-endpoint")
async def deprecated_endpoint():
raise HTTPException(
status_code=410,
detail="This endpoint is deprecated and will not be restored"
)
413 Payload Too Large
Файл или данные слишком большие.
# Проверяем размер файла
MAX_FILE_SIZE = 10 * 1024 * 1024 # 10 MB
@app.post("/upload")
async def upload_file(file: UploadFile):
if file.size > MAX_FILE_SIZE:
raise HTTPException(
status_code=413,
detail=f"File size exceeds limit of {MAX_FILE_SIZE} bytes"
)
# Обработка файла
return {"filename": file.filename}
415 Unsupported Media Type
Клиент отправил данные в неподдерживаемом формате.
from fastapi import Header
@app.post("/data")
async def process_data(
content_type: str = Header(None)
):
supported_types = ['application/json', 'application/xml']
if content_type not in supported_types:
raise HTTPException(
status_code=415,
detail=f"Unsupported media type: {content_type}"
)
# Обработка
return {"status": "ok"}
422 Unprocessable Entity
Данные синтаксически верны, но не соответствуют бизнес-логике.
from pydantic import BaseModel, field_validator
class OrderCreate(BaseModel):
quantity: int
price: float
@field_validator('quantity')
def quantity_must_be_positive(cls, v):
if v <= 0:
raise ValueError('Quantity must be greater than 0')
return v
@app.post("/orders")
async def create_order(order: OrderCreate):
# Pydantic автоматически вернет 422 при ошибке валидации
return {"quantity": order.quantity, "price": order.price}
# Примеры:
# POST /orders {"quantity": 5, "price": 100} -> 201
# POST /orders {"quantity": -5, "price": 100} -> 422
429 Too Many Requests
Клиент отправил слишком много запросов (rate limiting).
from slowapi import Limiter
from slowapi.util import get_remote_address
from slowapi.errors import RateLimitExceeded
limiter = Limiter(key_func=get_remote_address)
app.state.limiter = limiter
app.add_exception_handler(RateLimitExceeded, custom_rate_limit_exceeded_handler)
@app.get("/search")
@limiter.limit("10/minute") # 10 запросов в минуту
async def search(q: str, request: Request):
return {"results": search_results(q)}
# Примеры:
# GET /search?q=python (первые 10 запросов в минуту) -> 200
# GET /search?q=python (11-й запрос в минуту) -> 429 Too Many Requests
Таблица 4xx кодов
| Код | Название | Когда использовать | Пример |
|---|---|---|---|
| 400 | Bad Request | Неправильный формат данных | {"age": "abc"} вместо числа |
| 401 | Unauthorized | Нет аутентификации | Отсутствует JWT токен |
| 403 | Forbidden | Есть аутентификация, но нет прав | User пытается удалить чужой ресурс |
| 404 | Not Found | Ресурс не существует | GET /users/999 |
| 405 | Method Not Allowed | Неправильный HTTP метод | POST на GET endpoint |
| 409 | Conflict | Конфликт данных | Дублирование email при регистрации |
| 410 | Gone | Ресурс удален постоянно | Старая API версия |
| 413 | Payload Too Large | Данные слишком большие | Файл > max_size |
| 415 | Unsupported Media Type | Неподдерживаемый формат | Content-Type: text/plain вместо JSON |
| 422 | Unprocessable Entity | Данные не соответствуют бизнес-логике | Отрицательное количество |
| 429 | Too Many Requests | Rate limiting | Слишком много запросов за короткое время |
Best Practices для обработки 4xx
- Возвращай структурированный ответ:
class ErrorResponse(BaseModel):
error: str
error_code: str
details: Optional[dict] = None
request_id: str # Для отслеживания
# Использование
raise HTTPException(
status_code=400,
detail=ErrorResponse(
error="Invalid email format",
error_code="INVALID_EMAIL",
request_id=request.state.request_id
).dict()
)
-
Логируй все 4xx ошибки для аналитики и дебага
-
Не выдавай конфиденциальную информацию в 4xx ответах
-
Помогай клиенту — чёткие сообщения об ошибках
-
Используй правильный код — не все ошибки — 400
Разница между 4xx и 5xx
- 4xx (Client Error) — ошибка клиента, его вина. Обычно повтор не поможет.
- 5xx (Server Error) — ошибка сервера, можно попробовать позже.
Это важно для клиентов и систем мониторинга!