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

Какие знаешь основные принципы RESTful сервиса?

1.3 Junior🔥 221 комментариев
#REST API и HTTP

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

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

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

Основные принципы RESTful сервиса

REST (Representational State Transfer) — это архитектурный стиль для проектирования сетевых приложений. REST основывается на простых и понятных принципах, которые делают сервисы масштабируемыми и легкими в использовании.

1. Клиент-серверная архитектура

Клиент и сервер — это независимые компоненты, которые взаимодействуют через HTTP:

# Пример Flask сервера
from flask import Flask, jsonify, request

app = Flask(__name__)

@app.route('/api/v1/users', methods=['GET'])
def get_users():
    return jsonify([{"id": 1, "name": "Иван"}])

@app.route('/api/v1/users', methods=['POST'])
def create_user():
    data = request.get_json()
    return jsonify({"id": 2, "name": data["name"]}), 201

2. Ресурсы как основная единица

Всё в API представлено как ресурсы (существительные), не как действия (глаголы):

Правильно (RESTful):
  GET    /api/v1/users           # Список пользователей
  POST   /api/v1/users           # Создать пользователя
  GET    /api/v1/users/{id}      # Получить пользователя
  PUT    /api/v1/users/{id}      # Обновить пользователя
  DELETE /api/v1/users/{id}      # Удалить пользователя

Неправильно:
  GET    /api/v1/getUsers        # Глагол в URL!
  POST   /api/v1/createUser      # Глагол в URL!
  GET    /api/v1/deleteUser/{id} # Глагол в URL!

3. Использование HTTP методов правильно

Каждый HTTP метод имеет свою семантику:

from fastapi import FastAPI, HTTPException, status

app = FastAPI()

database = {1: {"id": 1, "name": "Иван", "email": "ivan@example.com"}}

# GET — безопасный, идемпотентный, не изменяет состояние
@app.get("/users/{user_id}")
def read_user(user_id: int):
    if user_id not in database:
        raise HTTPException(status_code=404, detail="User not found")
    return database[user_id]

# POST — создание нового ресурса, не идемпотентный
@app.post("/users", status_code=201)
def create_user(name: str, email: str):
    new_id = max(database.keys()) + 1
    database[new_id] = {"id": new_id, "name": name, "email": email}
    return database[new_id]

# PUT — полное обновление ресурса, идемпотентный
@app.put("/users/{user_id}")
def update_user(user_id: int, name: str, email: str):
    if user_id not in database:
        raise HTTPException(status_code=404)
    database[user_id] = {"id": user_id, "name": name, "email": email}
    return database[user_id]

# PATCH — частичное обновление
@app.patch("/users/{user_id}")
def patch_user(user_id: int, name: str = None):
    if user_id not in database:
        raise HTTPException(status_code=404)
    if name:
        database[user_id]["name"] = name
    return database[user_id]

# DELETE — удаление ресурса, идемпотентный
@app.delete("/users/{user_id}", status_code=204)
def delete_user(user_id: int):
    if user_id not in database:
        raise HTTPException(status_code=404)
    del database[user_id]

4. Stateless (без состояния)

Сервер не должен хранить контекст сессии клиента между запросами. Каждый запрос содержит все необходимо информацию:

# Хорошо — каждый запрос независим
@app.get("/users/{user_id}")
def get_user(user_id: int, authorization: str = Header(None)):
    # Верифицируем токен в каждом запросе
    if not verify_token(authorization):
        raise HTTPException(status_code=401)
    return get_user_data(user_id)

# Плохо — сервер помнит состояние
# sessions = {}  # Глобальное хранилище!
# @app.get("/users/{user_id}")
# def get_user(user_id, session_id):
#     if session_id not in sessions:
#         return {"error": "not authenticated"}

5. Кэширование

Ответы должны быть помечены как кэшируемые или некэшируемые:

from fastapi import FastAPI
from fastapi.responses import Response

app = FastAPI()

@app.get("/posts/{post_id}")
def get_post(post_id: int):
    # Этот ресурс можно кэшировать на 1 час
    response = Response({"id": post_id, "title": "Статья"})
    response.headers["Cache-Control"] = "public, max-age=3600"
    return response

@app.get("/user-profile")
def get_profile():
    # Личные данные не кэшируем
    response = Response({"name": "Иван"})
    response.headers["Cache-Control"] = "private, no-cache"
    return response

6. Использование правильных HTTP статус кодов

from fastapi import FastAPI, HTTPException

app = FastAPI()

@app.post("/users")
def create_user(name: str):
    # 201 Created — ресурс создан
    return {"id": 1, "name": name}, 201

@app.get("/users/{user_id}")
def get_user(user_id: int):
    if user_id == 999:
        # 404 Not Found — ресурс не найден
        raise HTTPException(status_code=404, detail="User not found")
    # 200 OK — успешно
    return {"id": user_id, "name": "Иван"}

@app.delete("/users/{user_id}")
def delete_user(user_id: int):
    # 204 No Content — успешно, но нет контента
    return {}, 204

@app.post("/login")
def login(username: str, password: str):
    if not verify_credentials(username, password):
        # 401 Unauthorized — не аутентифицирован
        raise HTTPException(status_code=401)
    # 200 OK с токеном
    return {"token": "jwt_token"}

@app.get("/admin")
def admin_panel():
    # 403 Forbidden — аутентифицирован, но нет доступа
    raise HTTPException(status_code=403, detail="Admin access required")

7. Версионирование API

from fastapi import FastAPI

app = FastAPI()

# v1 API
@app.get("/api/v1/users")
def get_users_v1():
    return [{"id": 1, "name": "Иван"}]

# v2 API с другой структурой
@app.get("/api/v2/users")
def get_users_v2():
    return [{"id": 1, "name": "Иван", "email": "ivan@example.com", "created_at": "2025-01-01"}]

8. Вложенные ресурсы

# Вложенный ресурс для коллекции
GET /api/v1/users/{user_id}/posts        # Посты пользователя
GET /api/v1/users/{user_id}/posts/{id}   # Конкретный пост

# Операции над конкретным ресурсом
POST /api/v1/posts/{id}/like             # Лайк поста
DELETE /api/v1/posts/{id}/like           # Убрать лайк

9. Фильтрация и сортировка

@app.get("/posts")
def list_posts(skip: int = 0, limit: int = 10, sort: str = "date"):
    # GET /api/v1/posts?skip=0&limit=10&sort=date
    return {"posts": [], "total": 100}

@app.get("/users")
def search_users(q: str = "", role: str = None):
    # GET /api/v1/users?q=ivan&role=admin
    return {"users": []}

Резюме

  • Ресурсы как существительные — /users, не /getUsers
  • Правильные HTTP методы — GET, POST, PUT, DELETE, PATCH
  • Stateless — сервер не хранит сессии
  • Правильные статус коды — 200, 201, 204, 400, 401, 403, 404
  • Кэширование — указывай Cache-Control заголовки
  • Версионирование — /api/v1/, /api/v2/
  • Вложенные ресурсы — /users/{id}/posts
  • Фильтрация — query параметры для поиска и сортировки
Какие знаешь основные принципы RESTful сервиса? | PrepBro