Какой ответ следует возвращать клиенту при неуспешной валидации запроса в ASP.NET Core?

Ответ

Стандартной и рекомендуемой практикой является возврат HTTP-статуса 400 Bad Request с детализированным объектом, описывающим ошибки валидации. В ASP.NET Core для этого есть встроенные механизмы.

Самый простой способ — использовать ControllerBase.ValidationProblem():

[HttpPost]
public IActionResult CreateProduct([FromBody] ProductCreateDto dto)
{
    if (!ModelState.IsValid)
    {
        // Автоматически возвращает 400 с объектом ProblemDetails
        return ValidationProblem(ModelState);
    }
    // Логика обработки валидного запроса...
}

Для большего контроля можно явно возвращать BadRequest(Object):

if (!ModelState.IsValid)
{
    return BadRequest(new
    {
        Status = 400,
        Title = "Validation Failed",
        Errors = ModelState.Values
            .SelectMany(v => v.Errors.Select(e => e.ErrorMessage))
    });
}

Стандартизированный ответ (используя ProblemDetails): Контроллер автоматически форматирует ответ в стандартном формате RFC 7807 (Problem Details):

{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "errors": {
    "Email": ["The Email field is required.", "The Email field is not a valid e-mail address."],
    "Age": ["The field Age must be between 18 and 100."]
  }
}

Ключевые принципы:

  1. Статус 400: Четко указывает клиенту на ошибку в запросе.
  2. Детализация ошибок: Указывает конкретные поля и сообщения, что позволяет клиенту исправить запрос.
  3. Консистентность: Использование стандартного формата (ProblemDetails) помогает клиентам единообразно обрабатывать ошибки.
  4. Безопасность: Никогда не возвращайте стек-трейсы или внутренние детали исключений.

Ответ 18+ 🔞

Давай разберём эту тему про валидацию в ASP.NET Core, а то некоторые возвращают ошибки как бог на душу положит, прям пиздец.

Вот смотри, стандартная и самая правильная практика — это вернуть статус 400 Bad Request с нормальным, человекочитаемым объектом, где расписаны все ошибки. В ASP.NET Core для этого уже всё придумано, не надо велосипед изобретать.

Самый простой и правильный способ — использовать ValidationProblem():

[HttpPost]
public IActionResult CreateProduct([FromBody] ProductCreateDto dto)
{
    if (!ModelState.IsValid)
    {
        // Автоматически возвращает 400 с объектом ProblemDetails
        return ValidationProblem(ModelState);
    }
    // Логика обработки валидного запроса...
}

Вот и всё, блядь. Одна строчка. Не надо тут писать свои костыли на коленке.

Если тебе прям очень хочется поконтролировать всё, можно явно через BadRequest:
Но это уже для особых ценителей, которым неймётся.

if (!ModelState.IsValid)
{
    return BadRequest(new
    {
        Status = 400,
        Title = "Validation Failed",
        Errors = ModelState.Values
            .SelectMany(v => v.Errors.Select(e => e.ErrorMessage))
    });
}

А вообще, фреймворк умный, он сам всё красиво упакует в стандартный формат RFC 7807 (Problem Details):

{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "errors": {
    "Email": ["The Email field is required.", "The Email field is not a valid e-mail address."],
    "Age": ["The field Age must be between 18 and 100."]
  }
}

Клиент посмотрит на это и сразу поймёт, где он накосячил, а не будет гадать на кофейной гуще.

Главные принципы, которые надо запомнить, чтобы не выглядеть как полный распиздяй:

  1. Статус 400 — это как красная лампочка для клиента: "чувак, ты прислал какую-то хуйню, исправляй".
  2. Детализация ошибок — покажи конкретно, какое поле и в чём провинилось. Не "что-то не так", а "Email — обязательное поле, ёпта".
  3. Консистентность — используй стандартный формат, чтобы у клиента не ехала крыша от разнобоя в ответах.
  4. Безопасность — это самое важное, блядь. Никогда, слышишь, никогда не возвращай стек-трейсы или внутренние детали исключений в проде. Это просто подарок для всяких уёбков, которые норовят систему поломать. Держи свои косяки при себе.

Вот и вся наука. Не усложняй, используй что дали, и будет тебе счастье.