Какой контроллер считается RESTful в ASP.NET Core?

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

Ответ

RESTful-контроллер в ASP.NET Core — это контроллер, который следует принципам архитектуры REST (Representational State Transfer). Его ключевые характеристики:

  • Использует HTTP-методы для операций CRUD:
    • GET — получение ресурса или коллекции.
    • POST — создание нового ресурса.
    • PUT — полное обновление ресурса.
    • PATCH — частичное обновление ресурса.
    • DELETE — удаление ресурса.
  • Использует маршрутизацию на основе ресурсов (RESTful-маршруты). Имена методов контроллера часто соответствуют шаблонам действий (например, Get(), Post([FromBody] Model model)), но основную роль играют атрибуты HTTP-методов.
  • Возвращает соответствующие коды состояния HTTP (200 OK, 201 Created, 404 Not Found и т.д.).
  • Работает с представлениями данных (DTO/ViewModel), а не напрямую с доменными моделями.
  • Является stateless — каждому запросу передается вся необходимая информация.

Пример RESTful-контроллера для сущности Product:

[ApiController]
[Route("api/[controller]")] // Маршрут: /api/products
public class ProductsController : ControllerBase
{
    private readonly IProductService _service;

    public ProductsController(IProductService service)
    {
        _service = service;
    }

    // GET: api/products
    [HttpGet]
    public async Task<ActionResult<IEnumerable<ProductDto>>> GetProducts()
    {
        var products = await _service.GetAllAsync();
        return Ok(products); // 200 OK
    }

    // GET: api/products/5
    [HttpGet("{id}")]
    public async Task<ActionResult<ProductDto>> GetProduct(int id)
    {
        var product = await _service.GetByIdAsync(id);
        if (product == null)
        {
            return NotFound(); // 404 Not Found
        }
        return Ok(product); // 200 OK
    }

    // POST: api/products
    [HttpPost]
    public async Task<ActionResult<ProductDto>> PostProduct(ProductCreateDto createDto)
    {
        var createdProduct = await _service.CreateAsync(createDto);
        // Возвращаем созданный ресурс и его местоположение
        return CreatedAtAction(nameof(GetProduct), 
                               new { id = createdProduct.Id }, 
                               createdProduct); // 201 Created
    }

    // PUT: api/products/5
    [HttpPut("{id}")]
    public async Task<IActionResult> PutProduct(int id, ProductUpdateDto updateDto)
    {
        if (id != updateDto.Id)
        {
            return BadRequest(); // 400 Bad Request
        }
        await _service.UpdateAsync(updateDto);
        return NoContent(); // 204 No Content
    }

    // DELETE: api/products/5
    [HttpDelete("{id}")]
    public async Task<IActionResult> DeleteProduct(int id)
    {
        await _service.DeleteAsync(id);
        return NoContent(); // 204 No Content
    }
}

Использование атрибута [ApiController] автоматически включает такие функции, как проверка модели (возврат 400 при невалидных данных) и привязка параметров из тела запроса.