Ответ
Стандартной и рекомендуемой практикой является возврат 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."]
}
}
Ключевые принципы:
- Статус 400: Четко указывает клиенту на ошибку в запросе.
- Детализация ошибок: Указывает конкретные поля и сообщения, что позволяет клиенту исправить запрос.
- Консистентность: Использование стандартного формата (
ProblemDetails) помогает клиентам единообразно обрабатывать ошибки. - Безопасность: Никогда не возвращайте стек-трейсы или внутренние детали исключений.
Ответ 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."]
}
}
Клиент посмотрит на это и сразу поймёт, где он накосячил, а не будет гадать на кофейной гуще.
Главные принципы, которые надо запомнить, чтобы не выглядеть как полный распиздяй:
- Статус 400 — это как красная лампочка для клиента: "чувак, ты прислал какую-то хуйню, исправляй".
- Детализация ошибок — покажи конкретно, какое поле и в чём провинилось. Не "что-то не так", а "Email — обязательное поле, ёпта".
- Консистентность — используй стандартный формат, чтобы у клиента не ехала крыша от разнобоя в ответах.
- Безопасность — это самое важное, блядь. Никогда, слышишь, никогда не возвращай стек-трейсы или внутренние детали исключений в проде. Это просто подарок для всяких уёбков, которые норовят систему поломать. Держи свои косяки при себе.
Вот и вся наука. Не усложняй, используй что дали, и будет тебе счастье.