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

Что означают различные коды статусов HTTP, такие как 100, 200, 300, 400 и другие?

1.0 Junior🔥 151 комментариев
#Git и VCS#Soft Skills

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

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

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

Полное руководство по HTTP статус кодам

HTTP статус коды сообщают о результате запроса. Они разделены на 5 категорий по первой цифре.

1xx: Informational (100-199)

Времённые ответы, информирующие о промежуточном состоянии запроса.

100 Continue — сервер готов получать тело запроса
101 Switching Protocols — сервер переходит на другой протокол (WebSocket)
102 Processing — сервер обрабатывает запрос (WebDAV)

Практический пример:

from fastapi import FastAPI
from fastapi.responses import StreamingResponse

app = FastAPI()

@app.post("/upload")
async def upload_large_file(request: Request):
    # Клиент отправит файл большого размера
    # 100 Continue уже обработан FastAPI автоматически
    body = await request.body()
    return {"size": len(body)}

Использование в WebSocket:

from fastapi import WebSocket

@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
    # 101 Switching Protocols происходит автоматически
    await websocket.accept()  # Это инициирует переключение на WebSocket
    while True:
        data = await websocket.receive_text()
        await websocket.send_text(f"Echo: {data}")

2xx: Success (200-299)

Запрос успешно выполнен. Это то, что хочет видеть клиент.

200 OK — стандартный успешный ответ
201 Created — ресурс был создан
202 Accepted — запрос принят, но обрабатывается асинхронно
204 No Content — успех, но нет тела ответа
206 Partial Content — только часть контента (для Range requests)

Примеры:

from fastapi import status
from fastapi.responses import Response

@app.get("/items")
async def get_items():
    # 200 OK (по умолчанию для GET)
    return [{"id": 1, "name": "Item 1"}]

@app.post("/items", status_code=status.HTTP_201_CREATED)
async def create_item(item: Item):
    # 201 Created — ресурс был создан
    return {"id": 1, "name": item.name}

@app.post("/background-job", status_code=status.HTTP_202_ACCEPTED)
async def start_background_job(job_data: dict):
    # 202 Accepted — запрос в очереди, обработается позже
    # asyncio.create_task(process_job(job_data))
    return {"job_id": "abc-123", "status": "queued"}

@app.delete("/items/{item_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_item(item_id: int):
    # 204 No Content — успех, но нечего возвращать
    # db.delete(item)
    return None  # Важно: нет тела ответа!

@app.get("/large-file")
async def get_large_file(range: str = Header(None)):
    # 206 Partial Content — для больших файлов
    if range:
        # Парсим Range header и возвращаем только нужную часть
        return Response(
            content=file_content,
            status_code=206,
            headers={"Content-Range": "bytes 0-1023/10000"}
        )
    return {"error": "Use Range header"}

3xx: Redirection (300-399)

Ресурс находится в другом месте. Клиент должен сделать дополнительный запрос.

300 Multiple Choices — несколько вариантов (редко)
301 Moved Permanently — ресурс перемещен постоянно
302 Found — ресурс временно в другом месте
304 Not Modified — кэшированная версия актуальна
307 Temporary Redirect — временный редирект (сохраняет метод)
308 Permanent Redirect — постоянный редирект (сохраняет метод)

Примеры:

from fastapi.responses import RedirectResponse
from fastapi import status

@app.get("/old-endpoint")
async def old_endpoint():
    # 301 Moved Permanently — SEO-friendly redirect
    return RedirectResponse(
        url="/new-endpoint",
        status_code=status.HTTP_301_MOVED_PERMANENTLY
    )

@app.get("/temporary")
async def temporary_redirect():
    # 302 Found — временный редирект
    return RedirectResponse(
        url="/new-location",
        status_code=status.HTTP_302_FOUND
    )

@app.get("/files/{file_path}")
async def serve_file(file_path: str, request: Request):
    # 304 Not Modified — если клиент имеет актуальный кэш
    etag = request.headers.get("If-None-Match")
    current_etag = f'\"version-123\"'  # Версия файла
    
    if etag == current_etag:
        return Response(status_code=status.HTTP_304_NOT_MODIFIED)
    
    # Иначе возвращаем файл с 200
    return FileResponse(
        file_path,
        headers={"ETag": current_etag}
    )

@app.post("/form-redirect")
async def form_redirect():
    # 307 Temporary Redirect — сохраняет POST метод при редиректе
    return RedirectResponse(
        url="/success",
        status_code=status.HTTP_307_TEMPORARY_REDIRECT
    )

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 — неправильный Content-Type
422 Unprocessable Entity — валидация не прошла
429 Too Many Requests — rate limiting

Примеры см. в предыдущем ответе о 4xx кодах

5xx: Server Error (500-599)

Ошибка сервера. Обычно можно повторить запрос позже.

500 Internal Server Error — неожиданная ошибка на сервере
501 Not Implemented — функция не реализована
502 Bad Gateway — шлюз получил неправильный ответ от upstream
503 Service Unavailable — сервер временно недоступен
504 Gateway Timeout — timeout ответа от upstream

Примеры:

from fastapi import FastAPI
from fastapi.exceptions import HTTPException
from fastapi.responses import JSONResponse

app = FastAPI()

@app.get("/items/{item_id}")
async def get_item(item_id: int):
    try:
        result = expensive_operation(item_id)
        return result
    except DatabaseError:
        # 500 Internal Server Error
        raise HTTPException(
            status_code=500,
            detail="Database error, please try again later"
        )

@app.get("/not-implemented")
async def not_implemented():
    # 501 Not Implemented
    raise HTTPException(
        status_code=501,
        detail="This feature is not implemented yet"
    )

@app.get("/upstream-call")
async def call_upstream():
    try:
        response = await httpx.get("https://external-service.com/api", timeout=5)
        return response.json()
    except httpx.TimeoutException:
        # 504 Gateway Timeout
        raise HTTPException(
            status_code=504,
            detail="Upstream service timeout"
        )
    except Exception:
        # 502 Bad Gateway
        raise HTTPException(
            status_code=502,
            detail="Upstream service error"
        )

# Обработчик для 503 Service Unavailable
@app.get("/status")
async def service_status(db: Session = Depends(get_db)):
    try:
        db.execute(text("SELECT 1"))
        return {"status": "operational"}
    except:
        # 503 Service Unavailable
        raise HTTPException(
            status_code=503,
            detail="Service temporarily unavailable"
        )

# Глобальный обработчик ошибок
@app.exception_handler(Exception)
async def global_exception_handler(request, exc):
    logger.error(f"Unhandled exception: {exc}")
    return JSONResponse(
        status_code=500,
        content={"error": "Internal server error"}
    )

Таблица всех кодов

КатегорияКодыЗначениеДействие клиента
1xx100-199ИнформацияЖдёт или продолжает
2xx200-299УспехИспользует ответ
3xx300-399РедиректПереходит по ссылке
4xx400-499Ошибка клиентаИсправляет запрос
5xx500-599Ошибка сервераПовторяет позже

Выбор правильного кода

Алгоритм выбора:

Запрос выполнен успешно?
├─ Да
│  ├─ Есть ли результат?
│  │  ├─ Да → 200 OK
│  │  └─ Нет → 204 No Content
│  ├─ Был ли создан ресурс? → 201 Created
│  └─ Запрос в очереди? → 202 Accepted
│
├─ Нет, клиент должен сделать другой запрос?
│  ├─ Ресурс в другом месте?
│  │  ├─ Постоянно → 301/308
│  │  └─ Временно → 302/307
│  └─ Актуальный кэш? → 304
│
├─ Нет, ошибка клиента?
│  ├─ Неправильный формат → 400
│  ├─ Нет аутентификации → 401
│  ├─ Нет прав → 403
│  ├─ Не существует → 404
│  ├─ Конфликт → 409
│  ├─ Валидация → 422
│  ├─ Rate limit → 429
│  └─ Другое → 400
│
└─ Нет, ошибка сервера?
   ├─ Неожиданная ошибка → 500
   ├─ Не реализовано → 501
   ├─ Upstream ошибка → 502
   ├─ Сервер недоступен → 503
   └─ Timeout → 504

Best Practices

  1. Выбирай точный код — не все ошибки это 400
  2. Возвращай тело ответа — объясни что случилось (кроме 204, 304)
  3. Логируй 5xx ошибки — это реальные проблемы
  4. Не логируй 4xx — это обычно вина клиента
  5. Используй стандарты — не выдумывай свои коды
  6. Помогай клиенту — чёткие сообщения об ошибках
  7. Возвращай скорость — правильный код помогает клиентам оптимизироваться

Примеры из реальных проектов

# Правильное обращение к ошибкам
class APIResponse:
    def success(data=None, status=200):
        return {"success": True, "data": data, "status": status}
    
    def error(message: str, code: int = 400, details=None):
        return {
            "success": False,
            "error": message,
            "code": code,
            "details": details
        }

# Использование
@app.post("/users")
async def create_user(user: UserCreate):
    if not is_email_valid(user.email):
        return APIResponse.error(
            message="Invalid email format",
            code=400
        ), 400
    
    if email_exists(user.email):
        return APIResponse.error(
            message="Email already registered",
            code=409
        ), 409
    
    new_user = db.create_user(user)
    return APIResponse.success(
        data=new_user.dict(),
        status=201
    ), 201