Почему ModelState возвращает другой результат и как это исправить?

#asp.net-mvc #validation #asp.net-core #asp.net-web-api #modelstate

#asp.net-mvc #проверка #asp.net-core #asp.net-web-api #modelstate

Вопрос:

Asp.net ядро 3.1 WebAPI.

У меня есть модель с требуемыми свойствами.

1. Если модель недопустима, то ответ содержит такие данные, как :

 {
    "errors": {
        "Name": [
            "Update model can't have all properties as null."
        ],
        "Color": [
            "Update model can't have all properties as null."
        ]
    },
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
    "title": "One or more validation errors occurred.",
    "status": 400,
    "traceId": "|f032f1c9-4c36d1e62aa60ead."
}
 

И это выглядит хорошо для меня.

Но если я добавлю некоторую пользовательскую проверку в ModelState.AddModelError(«StatusID», «Недопустимый идентификатор статуса заказа».) затем он возвращает другую структуру:

 [
    {
        "childNodes": null,
        "children": null,
        "key": "statusId",
        "subKey": {
            "buffer": "statusId",
            "offset": 0,
            "length": 8,
            "value": "statusId",
            "hasValue": true
        },
        "isContainerNode": false,
        "rawValue": "11202",
        "attemptedValue": "11202",
        "errors": [
            {
                "exception": null,
                "errorMessage": "Invalid order status id."
            }
        ],
        "validationState": 1
    }
]
 

Также выглядит как ModelState.isValid больше не актуален для контроллера, потому что неверный запрос возвращается еще до того, как он войдет в контроллер с недопустимым режимом. Или есть какой-то флаг с глобальной проверкой через ModelSate?

Почему структура отличается? Как сделать его таким же? Как заставить перейти к ModelState.Метод isValid внутри контроллера api, как он работал в MVC?

Обновить:

 

    [Route("....")]
    [Authorize]
    [ApiController]
    public class StatusesController : ApiControllerBase
    {


        [HttpPut, Route("{statusId}")]
        [ProducesResponseType(StatusCodes.Status200OK)]
        [ProducesResponseType(StatusCodes.Status400BadRequest)]
        [ProducesResponseType(StatusCodes.Status409Conflict)]
        [Produces("application/json")]
        public async Task<ObjectResult> UpdateStatusAsync(int statusId, [FromBody] StatusUpdateDto orderStatusUpdateDto)
        {

            int companyId = User.Identity.GetClaimValue<int>(ClaimTypes.CompanyId);

            const string errorNotFound  = "There is not order status with this id for such company";
            if (statusId <= 0)
            {
                Logger.LogError(errorNotFound);
                ModelState.AddErrorModel(nameof(statusId), "Invalid order status id")
                throw new NotFound(ModelState);
            }
            
            if (orderStatusUpdateDto == null)
            {
                const string error = "Invalid (null) order status can't be added";
                Logger.LogError(error);
                throw new ArgumentNullException(error);
            }

            if (ModelState.IsValid == false) // also this code is always true or returns 400 before this line
            {
                return BadRequest(ModelState); 
            }

           ....
            return resu<
        }

}
 

Комментарии:

1. Где вы добавляете свою ошибку в состояние модели? Внутри метода контроллера? Украшаете ли вы контроллер атрибутом ApiController?

2. Внутри метода контроллера — да украсьте контроллер ApiController — да

3. В дополнение к приведенному ниже ответу treze, вы можете настроить фабрику, используемую для получения результата недопустимой модели .ConfigureApiBehaviorOptions(o => o.InvalidModelStateResponseFactory = ...) .

Ответ №1:

ApiController Атрибут добавляет контроллеру некоторые специфические самоуверенные поведения. Одним из них является возврат ошибки 400, если модель недействительна. Это поведение можно отключить, но только на глобальном уровне.

 services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressModelStateInvalidFilter = true;
    });
 

Я думаю, у вас есть следующие варианты:

  • Отключите это поведение и проверьте ModelState.IsValid себя. Используйте ValidationProblem метод для получения того же ответа
  • Добавьте эту проверку в средство проверки модели
  • Оставьте все как есть. Но используйте ValidationProblem метод внутри контроллера для возврата ошибок проверки.

Смотрите https://docs.microsoft.com/en-us/aspnet/core/web-api/?view=aspnetcore-3.1#automatic-http-400-responses для получения дополнительной информации

Комментарии:

1. Спасибо. Это отвечает на некоторые из моих вопросов. Что вы думаете о другой структуре в возвращаемом результате?

2. Что вы возвращаете как ActionResult при добавлении пользовательской ошибки? Если вы вернете ValidationProblem() , вы должны получить тот же ответ. docs.microsoft.com/en-us/dotnet/api /…

3. Я исправил пример, если я возвращаю BadRequest или NotFound (если id меньше 0) и передаю ModelState в результат в обоих случаях

4. О … похоже, это причина! Как только я возвращаю NotFound — я получаю другую структуру результата, затем я возвращаю BadRequest (ожидаемая структура). Возможно, фабрика проверки имеет конкретную реализацию для плохих запросов и по умолчанию для других…

5. да, как указано в документах, на которые я ссылался в ответе «Чтобы сделать автоматические и пользовательские ответы согласованными, вызовите метод ValidationProblem вместо BadRequest . ValidationProblem возвращает объект ValidationProblemDetails, а также автоматический ответ. »