← Назад к вопросам

Что обозначают 400-ые коды ответов http?

1.0 Junior🔥 111 комментариев
#DevOps и инфраструктура#Django

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

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 кодов

КодНазваниеКогда использоватьПример
400Bad RequestНеправильный формат данных{"age": "abc"} вместо числа
401UnauthorizedНет аутентификацииОтсутствует JWT токен
403ForbiddenЕсть аутентификация, но нет правUser пытается удалить чужой ресурс
404Not FoundРесурс не существуетGET /users/999
405Method Not AllowedНеправильный HTTP методPOST на GET endpoint
409ConflictКонфликт данныхДублирование email при регистрации
410GoneРесурс удален постоянноСтарая API версия
413Payload Too LargeДанные слишком большиеФайл > max_size
415Unsupported Media TypeНеподдерживаемый форматContent-Type: text/plain вместо JSON
422Unprocessable EntityДанные не соответствуют бизнес-логикеОтрицательное количество
429Too Many RequestsRate limitingСлишком много запросов за короткое время

Best Practices для обработки 4xx

  1. Возвращай структурированный ответ:
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()
)
  1. Логируй все 4xx ошибки для аналитики и дебага

  2. Не выдавай конфиденциальную информацию в 4xx ответах

  3. Помогай клиенту — чёткие сообщения об ошибках

  4. Используй правильный код — не все ошибки — 400

Разница между 4xx и 5xx

  • 4xx (Client Error) — ошибка клиента, его вина. Обычно повтор не поможет.
  • 5xx (Server Error) — ошибка сервера, можно попробовать позже.

Это важно для клиентов и систем мониторинга!

Что обозначают 400-ые коды ответов http? | PrepBro