Привет! Статус-коды HTTP — это способ для сервера сказать клиенту, что произошло с его запросом. Каждый код имеет своё значение и помогает клиенту понять, как обрабатывать ответ.
В этой статье мы разберём:
- Успешные (2xx)
- Перенаправления (3xx)
- Ошибки клиента (4xx)
- Ошибки сервера (5xx)
- Примеры использования в FastAPI
- Что нужно знать перед началом
- Основная часть
- Общая классификация
- Успешные коды (2xx)
- Коды перенаправления (3xx)
- Ошибки клиента (4xx)
- Ошибки сервера (5xx)
- Использование status в FastAPI
- Полный пример
- Задачи для закрепления
- Нюансы и подводные камни
- Частые ошибки и как их избежать
- Ошибка 1: Возврат 200 при создании ресурса
- Ошибка 2: Путаница между 401 и 403
- Ошибка 3: Возврат 500 для ошибок клиента
- Шпаргалка
- Заключение
- КВИЗ
- Что дальше?
Что нужно знать перед началом
Для этого урока тебе понадобится:
- Базовое понимание HTTP и REST API
- Установленный FastAPI
Совет: Правильное использование статус-кодов делает API понятным и удобным.
Основная часть
Общая классификация
| Диапазон | Назначение |
|---|---|
| 1xx | Информационные |
| 2xx | Успех |
| 3xx | Перенаправление |
| 4xx | Ошибка клиента |
| 5xx | Ошибка сервера |
Успешные коды (2xx)
| Код | Название | Значение |
|---|---|---|
| 200 | OK | Успешный запрос |
| 201 | Created | Ресурс создан |
| 202 | Accepted | Запрос принят в обработку |
| 204 | No Content | Успех, но тело пустое |
Примеры в FastAPI:
from fastapi import FastAPI, status
from fastapi.responses import JSONResponse
app = FastAPI()
# 200 OK
@app.get("/users/{user_id}")
def get_user(user_id: int):
return {"id": user_id, "name": "Анна"} # По умолчанию 200
# 201 Created
@app.post("/users/")
def create_user(name: str):
return JSONResponse(
status_code=status.HTTP_201_CREATED,
content={"message": "Пользователь создан"}
)
# 204 No Content
@app.delete("/users/{user_id}")
def delete_user(user_id: int):
# Удаляем пользователя
return Response(status_code=status.HTTP_204_NO_CONTENT)Коды перенаправления (3xx)
| Код | Название | Значение |
|---|---|---|
| 301 | Moved Permanently | Ресурс перемещён навсегда |
| 302 | Found | Временное перенаправление |
| 304 | Not Modified | Ресурс не изменился |
Пример в FastAPI:
from fastapi.responses import RedirectResponse
@app.get("/old-page")
def old_page():
return RedirectResponse(url="/new-page", status_code=301)Ошибки клиента (4xx)
| Код | Название | Значение |
|---|---|---|
| 400 | Bad Request | Неверный запрос |
| 401 | Unauthorized | Требуется авторизация |
| 403 | Forbidden | Доступ запрещён |
| 404 | Not Found | Ресурс не найден |
| 405 | Method Not Allowed | Метод не поддерживается |
| 409 | Conflict | Конфликт данных |
| 422 | Unprocessable Entity | Ошибка валидации |
Примеры в FastAPI:
from fastapi import FastAPI, HTTPException
app = FastAPI()
# 404 Not Found
@app.get("/users/{user_id}")
def get_user(user_id: int):
if user_id not in users_db:
raise HTTPException(status_code=404, detail="Пользователь не найден")
return users_db[user_id]
# 400 Bad Request
@app.post("/users/")
def create_user(name: str, age: int):
if age < 0:
raise HTTPException(status_code=400, detail="Возраст не может быть отрицательным")
return {"message": "Пользователь создан"}
# 401 Unauthorized
@app.get("/admin/")
def admin_only(token: str):
if token != "secret":
raise HTTPException(status_code=401, detail="Неверный токен")
return {"message": "Добро пожаловать, администратор!"}
# 403 Forbidden
@app.get("/admin/users/")
def admin_users(user_id: int):
if user_id != 1: # Только пользователь с id=1 может просматривать
raise HTTPException(status_code=403, detail="Недостаточно прав")
return {"users": users_db}
# 409 Conflict
@app.post("/users/")
def create_user(name: str):
if any(user["name"] == name for user in users_db.values()):
raise HTTPException(status_code=409, detail="Пользователь с таким именем уже существует")
return {"message": "Пользователь создан"}
# 422 Unprocessable Entity (автоматически от FastAPI)
# Возникает при ошибках валидации PydanticОшибки сервера (5xx)
| Код | Название | Значение |
|---|---|---|
| 500 | Internal Server Error | Внутренняя ошибка сервера |
| 502 | Bad Gateway | Ошибка шлюза |
| 503 | Service Unavailable | Сервис недоступен |
Пример в FastAPI:
@app.get("/calculate/{value}")
def calculate(value: int):
try:
result = 100 / value
return {"result": result}
except ZeroDivisionError:
raise HTTPException(status_code=500, detail="Ошибка вычисления")Использование status в FastAPI
from fastapi import FastAPI, status
from fastapi.responses import JSONResponse
app = FastAPI()
@app.post("/users/")
def create_user(name: str):
return JSONResponse(
status_code=status.HTTP_201_CREATED,
content={"message": "Пользователь создан", "user": {"name": name}}
)Совет: Используй
status.HTTP_XXXвместо чисел — это делает код читаемее.
Полный пример
from fastapi import FastAPI, status, HTTPException
from fastapi.responses import JSONResponse
app = FastAPI()
users_db = {}
@app.get("/users/")
def get_users():
return {"users": list(users_db.values())}
@app.get("/users/{user_id}")
def get_user(user_id: int):
if user_id not in users_db:
raise HTTPException(status_code=404, detail="Пользователь не найден")
return users_db[user_id]
@app.post("/users/")
def create_user(name: str, age: int):
if age < 0:
raise HTTPException(status_code=400, detail="Возраст не может быть отрицательным")
user_id = len(users_db) + 1
users_db[user_id] = {"id": user_id, "name": name, "age": age}
return JSONResponse(
status_code=status.HTTP_201_CREATED,
content={"message": "Пользователь создан", "user": users_db[user_id]}
)
@app.put("/users/{user_id}")
def update_user(user_id: int, name: str):
if user_id not in users_db:
raise HTTPException(status_code=404, detail="Пользователь не найден")
users_db[user_id]["name"] = name
return {"message": "Пользователь обновлён"}
@app.delete("/users/{user_id}")
def delete_user(user_id: int):
if user_id not in users_db:
raise HTTPException(status_code=404, detail="Пользователь не найден")
del users_db[user_id]
return JSONResponse(
status_code=status.HTTP_204_NO_CONTENT,
content=None
)
@app.get("/admin/")
def admin(token: str):
if token != "secret":
raise HTTPException(status_code=401, detail="Неверный токен")
return {"message": "Добро пожаловать, администратор!"}Задачи для закрепления
Задача 1. Создай эндпоинт, возвращающий 201 при создании ресурса.
Задача 2. Создай эндпоинт, возвращающий 404 при отсутствии ресурса.
Задача 3. Создай эндпоинт, возвращающий 400 при неверных данных.
Задача 4. Создай эндпоинт, возвращающий 204 при удалении.
Задача 5. Создай эндпоинт, возвращающий 401 при неверном токене.
Задача 6. Создай эндпоинт, возвращающий 409 при конфликте данных.
Ответы:
Задача 1.
@app.post("/items/")
def create_item(name: str):
return JSONResponse(status_code=201, content={"message": "Создано"})Задача 2.
@app.get("/items/{item_id}")
def get_item(item_id: int):
if item_id not in items_db:
raise HTTPException(status_code=404, detail="Не найдено")
return items_db[item_id]Задача 3.
@app.post("/items/")
def create_item(name: str, price: float):
if price < 0:
raise HTTPException(status_code=400, detail="Цена не может быть отрицательной")
return {"message": "Создано"}Задача 4.
@app.delete("/items/{item_id}")
def delete_item(item_id: int):
return JSONResponse(status_code=204, content=None)Задача 5.
@app.get("/admin/")
def admin(token: str):
if token != "secret":
raise HTTPException(status_code=401, detail="Неверный токен")
return {"message": "Добро пожаловать"}Задача 6.
@app.post("/items/")
def create_item(name: str):
if any(item["name"] == name for item in items_db.values()):
raise HTTPException(status_code=409, detail="Товар с таким именем уже существует")
return {"message": "Создано"}Нюансы и подводные камни
- 2xx — успех. 200 OK, 201 Created, 204 No Content.
- 3xx — редиректы. 301 Moved Permanently, 302 Found.
- 4xx — ошибки клиента. 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 409 Conflict, 422 Unprocessable Entity.
- 5xx — ошибки сервера. 500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable.
- Используй правильные статус-коды для каждого случая.
- 401 и 403 — разные вещи. 401 — не авторизован, 403 — нет прав.
- 422 — ошибка валидации данных.
Частые ошибки и как их избежать
Ошибка 1: Возврат 200 при создании ресурса
Используй 201 Created для POST.
Ошибка 2: Путаница между 401 и 403
401 — не авторизован. 403 — доступ запрещён.
Ошибка 3: Возврат 500 для ошибок клиента
Используй 4xx для ошибок клиента.
Шпаргалка
| Код | Название | Использование |
|---|---|---|
| 200 | OK | Успешный GET/PUT |
| 201 | Created | Успешный POST |
| 204 | No Content | Успешный DELETE |
| 301 | Moved Permanently | Перенаправление |
| 400 | Bad Request | Неверные данные |
| 401 | Unauthorized | Нет авторизации |
| 403 | Forbidden | Доступ запрещён |
| 404 | Not Found | Ресурс не найден |
| 409 | Conflict | Конфликт данных |
| 422 | Unprocessable | Ошибка валидации |
| 500 | Internal Server | Ошибка сервера |
Заключение
Сегодня мы:
- Изучили статус-коды HTTP
- Разобрали 2xx, 3xx, 4xx, 5xx
- Примеры в FastAPI
- Практические рекомендации








