Какие принципы RESTful архитектуры вы знаете?

Ответ

REST (Representational State Transfer) — это набор архитектурных ограничений и принципов для построения распределенных веб-сервисов. Ключевые принципы:

  1. Единообразие интерфейса (Uniform Interface)

    • Идентификация ресурсов: Каждый ресурс (пользователь, заказ) однозначно идентифицируется URI (например, /api/users/123).
    • Манипуляция ресурсами через представления: Клиент работает с ресурсом через его представление (например, JSON). Отправляя обновленное представление, клиент изменяет ресурс.
    • Самодостаточные сообщения: Каждый запрос от клиента содержит всю информацию, необходимую серверу для его обработки (метод, заголовки, тело).
    • Гипермедиа как двигатель состояния приложения (HATEOAS): Ответы сервера содержат ссылки на другие доступные действия с ресурсом, что позволяет клиенту динамически обнаруживать функциональность API.
  2. Отсутствие состояния (Stateless) Сервер не хранит состояние клиента между запросами. Сессионные данные должны храниться на стороне клиента (например, в токене) и передаваться в каждом запросе.

  3. Кэшируемость (Cacheable) Ответы сервера должны явно указывать, можно ли их кэшировать и как долго (через HTTP-заголовки Cache-Control, Expires). Это критично для производительности.

  4. Клиент-серверная архитектура Четкое разделение ответственности: клиент отвечает за UI и пользовательский опыт, сервер — за обработку данных, хранение и бизнес-логику.

  5. Многоуровневая система (Layered System) Архитектура может состоять из нескольких уровней (балансировщики, прокси, шлюзы). Клиент не знает, взаимодействует ли он напрямую с конечным сервером или через промежуточное звено.

  6. Код по требованию (Code on Demand, опционально) Сервер может временно расширять функциональность клиента, передавая исполняемый код (например, JavaScript). На практике используется редко.

Практическое применение в ASP.NET Core:

[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    // GET api/products — получение коллекции (Read)
    [HttpGet]
    public ActionResult<IEnumerable<Product>> Get() { ... }

    // GET api/products/5 — получение элемента (Read)
    [HttpGet("{id}")]
    public ActionResult<Product> Get(int id) { ... }

    // POST api/products — создание нового (Create)
    [HttpPost]
    public ActionResult<Product> Post([FromBody] Product product) 
    { 
        // Возвращаем код 201 Created и URI нового ресурса
        return CreatedAtAction(nameof(Get), new { id = product.Id }, product);
    }

    // PUT api/products/5 — полное обновление (Update)
    [HttpPut("{id}")]
    public IActionResult Put(int id, [FromBody] Product product) { ... }

    // DELETE api/products/5 — удаление (Delete)
    [HttpDelete("{id}")]
    public IActionResult Delete(int id) { ... }
}

Важно: Настоящий REST подразумевает использование HATEOAS, но на практике многие публичные API следуют лишь части принципов (часто называя это "REST-like" или "RESTful"), фокусируясь на использовании HTTP-методов, статус-кодов и ресурсно-ориентированных URI.

Ответ 18+ 🔞

Давай разберём эту вашу REST-архитектуру, чтобы было понятно, как мужик за пивом ходит.

Смотри, REST — это, по сути, свод правил, как серверу с клиентами общаться, чтобы не было вот этой вот бардачной пьянки, когда один в кустах серит, а другой не знает, где искать. Это как договориться, что все бутылки из-под пива ставим в один ящик, а не кидаем по всей хате.

Основные принципы, если на пальцах:

  1. Единый интерфейс, блядь. Это самое главное. Все как в армии: у каждого предмета (ресурса) есть свой уникальный номер (URI). Хочешь получить данные о товаре с айдишником 5? Запрос на /api/products/5. Всё. Никаких «а дай-ка мне товар, ну который вчера смотрел, красненький такой». Серверу похуй, что ты вчера смотрел. Он без состояния, об этом ниже. А ещё в идеале, когда сервер тебе отвечает, он должен тебе в ответе дать ссылочки, что с этим ресурсом можно сделать дальше. Типа «держи данные о товаре, а если хочешь его удалить — вот тебе ссылка DELETE на этот же URL». Это называется HATEOAS, но его многие в рот ебут и не используют, потому что заморочно.

  2. Без состояния (Stateless). Это вообще краеугольный камень. Представь, сервер — это охуенно тупой, но исполнительный солдат. У него нет памяти. Каждый твой запрос — это отдельный приказ, в котором должно быть ВСЁ, что нужно для его выполнения. Нельзя прийти и сказать «ну я же тебе вчера говорил, кто я!». Не помнит он вчера! В каждом запросе ты должен слать свой токен или логин-пароль, чтобы он тебя опознал. Зато его легко масштабировать — поставил десять таких же тупых солдат, и им похуй, кто к кому обращался до этого.

  3. Кэшируемость. Чтобы серверу по сто раз на дню не отвечать на один и тот же дурацкий вопрос «а какие у вас товары?», он может сказать: «клиент, запомни ответ на пять минут». И следующие пять минут клиент берет ответ из своей памяти (кэша), а не дергает сервер. Серверу легче, трафик меньше. Всё через заголовки HTTP типа Cache-Control управляется.

  4. Клиент-сервер. Четкое разделение труда. Клиент (браузер, мобила) — это лицо, которое вертит хвостом, рисует кнопочки и собирает данные с пользователя. Сервер — это мозг и сейф: он думает, проверяет, хранит данные. Они общаются через API и не лезут в дела друг друга. Идеально.

  5. Многоуровневая система. Клиент может даже не знать, что он общается не напрямую с главным сервером, а через какого-нибудь посредника — балансировщик нагрузки, прокси или брандмауэр. Это как звонок в кол-центр: тебе похуй, в какой конкретно город дозвонился, главное — вопрос решили.

  6. Код по требованию (опционально). Это редкая дичь, когда сервер может сказать: «на, клиент, выполни этот скрипт на своей стороне». Типа как в браузер приходит JavaScript. В обычных API этого почти не встретишь, забей.

Как это выглядит в коде на ASP.NET Core, например:

[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    // GET api/products — получить список всех товаров (Read)
    [HttpGet]
    public ActionResult<IEnumerable<Product>> Get() { ... }

    // GET api/products/5 — получить один товар по ID (Read)
    [HttpGet("{id}")]
    public ActionResult<Product> Get(int id) { ... }

    // POST api/products — создать новый товар (Create)
    [HttpPost]
    public ActionResult<Product> Post([FromBody] Product product) 
    { 
        // Здесь обычно сохраняешь product в базу
        // А потом возвращаешь код 201 Created и ссылку на созданный ресурс
        // Это и есть часть того самого "единообразного интерфейса"
        return CreatedAtAction(nameof(Get), new { id = product.Id }, product);
    }

    // PUT api/products/5 — полностью обновить товар с ID=5 (Update)
    [HttpPut("{id}")]
    public IActionResult Put(int id, [FromBody] Product product) { ... }

    // DELETE api/products/5 — удалить товар с ID=5 (Delete)
    [HttpDelete("{id}")]
    public IActionResult Delete(int id) { ... }
}

Итог такой: если ты используешь HTTP-методы (GET, POST, PUT, DELETE) по смыслу, работаешь с ресурсами через URL и возвращаешь адекватные статус-коды (200, 201, 404, 500) — ты уже молодец. Это уже можно назвать RESTful. А вот эта вся философия с HATEOAS — это как высший пилотаж, до которого многие не допрыгивают, и живут. Главное — чтобы система работала, а не охуевала от твоего кривого API.