Обработка ошибок привязки модели при использовании [FromBody] в .NET Core 2.1 - PullRequest
1 голос
/ 11 марта 2019

Я пытаюсь понять, как я могу перехватывать и обрабатывать ошибки привязки модели в ядре .net.

Я хочу сделать это:

    // POST api/values
    [HttpPost]
    public void Post([FromBody] Thing value)
    {
        if (!ModelState.IsValid)
        {
            // Handle Error Here
        }
    }

Где модель "Вещи": ​​

public class Thing
{
    public string Description { get; set; }
    public int Amount { get; set; }
}

Однако, если я передам недопустимую сумму, такую ​​как:

{ 
   "description" : "Cats",
   "amount" : 21.25
}

Я получаю сообщение об ошибке, похожее на это:

{"amount": ["Входная строка '21 .25 'не является допустимым целым числом. Путь' amount ', строка 1, позиция 38."]}

Без указания кода контроллера.

Как я могу настроить отправку ошибки обратно? (так как в основном мне нужно обернуть эту ошибку сериализации в больший объект ошибки)

Ответы [ 2 ]

2 голосов
/ 11 марта 2019

Итак, я пропустил это раньше, но я нашел здесь:

https://docs.microsoft.com/en-us/aspnet/core/web-api/index?view=aspnetcore-2.2#automatic-http-400-responses

Это если вы используете

[ApiController] 

атрибут на вашем контроллере, он будет автоматически обрабатывать ошибки сериализации и предоставит 400 ответ, эквивалентный:

if (!ModelState.IsValid)
{
    return BadRequest(ModelState);
}

Вы можете отключить это поведение в Startup.cs следующим образом:

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

Если вы хотите настроить ответ, лучше использовать InvalidModelStateResponseFactory, который является делегатом, принимающим ActionContext и возвращающим IActionResult, который будет вызываться для обработки ошибок сериализации.

См. Этот пример:

services.Configure<ApiBehaviorOptions>(options =>
{
    options.InvalidModelStateResponseFactory = actionContext => 
    {
        var errors = actionContext.ModelState
            .Where(e => e.Value.Errors.Count > 0)
            .Select(e => new Error
            {
            Name = e.Key,
            Message = e.Value.Errors.First().ErrorMessage
            }).ToArray();

        return new BadRequestObjectResult(errors);
    }
});
0 голосов
/ 11 марта 2019

Фреймворк использует связыватели моделей для сопоставления строк запроса в сложный объект, поэтому я предполагаю, что вам нужно будет создать связыватель пользовательских моделей. Пожалуйста, обратитесь Связывание пользовательских моделей в ASP.Net Core

Но до этого проще было бы попробовать атрибуты Binder в ваших моделях. Атрибут BindRequired добавляет ошибку состояния модели, если привязка невозможна. Таким образом, вы можете изменить свою модель как:

public class Thing 
{
    [BindRequired]
    public string Description {get;set;}

    [BindRequired]
    public int Amount {get;set;}
}

Если это не сработает для вас, вы можете попытаться создать пользовательский механизм связывания моделей. Пример из статьи:

[ModelBinder(BinderType = typeof(AuthorEntityBinder))]
public class Author
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string GitHub { get; set; }
    public string Twitter { get; set; }
    public string BlogUrl { get; set; }
}

public class AuthorEntityBinder : IModelBinder
{
   private readonly AppDbContext _db;
   public AuthorEntityBinder(AppDbContext db)
   {
       _db = db;
   }

public Task BindModelAsync(ModelBindingContext bindingContext)
{
    if (bindingContext == null)
    {
        throw new ArgumentNullException(nameof(bindingContext));
    }

    var modelName = bindingContext.ModelName;

    // Try to fetch the value of the argument by name
    var valueProviderResult =
        bindingContext.ValueProvider.GetValue(modelName);

    if (valueProviderResult == ValueProviderResult.None)
    {
        return Task.CompletedTask;
    }

    bindingContext.ModelState.SetModelValue(modelName,
        valueProviderResult);

    var value = valueProviderResult.FirstValue;

    // Check if the argument value is null or empty
    if (string.IsNullOrEmpty(value))
    {
        return Task.CompletedTask;
    }

    int id = 0;
    if (!int.TryParse(value, out id))
    {
        // Non-integer arguments result in model state errors
        bindingContext.ModelState.TryAddModelError(
                                modelName,
                                "Author Id must be an integer.");
        return Task.CompletedTask;
    }

    // Model will be null if not found, including for 
    // out of range id values (0, -3, etc.)
    var model = _db.Authors.Find(id);
    bindingContext.Result = ModelBindingResult.Success(model);
    return Task.CompletedTask;
   }
}

Возможно, вы также захотите взглянуть на Проверка модели

...