По каким критериям API можно назвать RESTful?

«По каким критериям API можно назвать RESTful?» — вопрос из категории Сети, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

API считается RESTful, если он следует архитектурным ограничениям и принципам REST (Representational State Transfer):

  1. Единообразие интерфейса (Uniform Interface):
    • Ресурсы: Данные представлены как ресурсы с уникальными идентификаторами (URI), например, /api/users/123.
    • Манипуляции через представления: Клиент манипулирует ресурсом через представление (например, JSON), которое содержит достаточно информации для этого.
    • Самодостаточные сообщения: Каждый запрос содержит всю информацию, необходимую для его обработки.
    • HATEOAS (Hypermedia as the Engine of Application State): В идеале, ответы API содержат гиперссылки на возможные следующие действия, что делает API более открытым для обнаружения.
  2. Отсутствие состояния (Stateless): Сервер не хранит состояние клиента между запросами. Весь необходимый контекст (например, токен аутентификации) должен передаваться в каждом запросе.
  3. Кэшируемость (Cacheable): Ответы сервера должны явно указывать, можно ли их кэшировать и как долго, чтобы повысить производительность.
  4. Клиент-серверная архитектура: Четкое разделение обязанностей позволяет им развиваться независимо.
  5. Слоистая система (Layered System): Архитектура может включать промежуточные серверы (балансировщики, прокси, шлюзы), не влияя на взаимодействие клиента и сервера.
  6. Использование стандартных HTTP-методов:
    • GET — получение ресурса.
    • POST — создание ресурса.
    • PUT/PATCH — полное или частичное обновление ресурса.
    • DELETE — удаление ресурса.

Пример RESTful эндпоинта на ASP.NET Core:

[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
    [HttpGet("{id}")] // GET /api/users/5
    public ActionResult<UserDto> GetUser(int id)
    {
        var user = _repository.GetUser(id);
        if (user == null) return NotFound(); // Корректный HTTP-статус 404
        return Ok(user); // Статус 200 с данными
    }

    [HttpPost] // POST /api/users
    public ActionResult<UserDto> CreateUser([FromBody] CreateUserDto userDto)
    {
        var newUser = _repository.AddUser(userDto);
        return CreatedAtAction(nameof(GetUser), 
                               new { id = newUser.Id }, 
                               newUser); // Статус 201 с заголовком Location
    }
}