Проблема с привязкой модели к POST в API - PullRequest
0 голосов
/ 28 августа 2018

У меня проблемы с пониманием процесса связывания модели в ядре Asp.Net 2. У меня очень простой API с моделью. У него есть некоторая базовая проверка. Всякий раз, когда пользователь публикует неверную модель, я пытаюсь вернуть 422 необработанного объекта вместе с сообщениями об ошибках из состояния модели.

2 проблемы, которые я пытаюсь понять, следующие:

  • Если я отправляю запрос без идентификатора, создается идентификатор по умолчанию 0, обходящий обязательный атрибут. Я предполагаю, что это функциональность C # для предоставления значений по умолчанию для полей. Есть ли способ обойти это?

  • Другая проблема заключается в том, что если я добавлю точку останова в свое почтовое действие и отправлю неверный запрос, он даже не перейдет в метод. Он отправляет обратно 400 неверных запросов с использованием атрибутов проверки. Как это работает? Останавливается ли запрос, как только он пытается связать модель с недопустимым свойством (т. Е. Длина имени> 10)? Что мне нужно сделать, это отправить обратно 422 необработанного объекта с тем же сообщением об ошибке вместо 400.

Не входит ли ASP.NET даже в метод, если проверка состояния модели завершается неудачно на основании атрибутов проверки? Что может быть лучше для решения этой проблемы, чтобы вернуть код ошибки 422?

Ниже приведен код для моих различных классов (я использовал шаблон API при создании проекта):

Startup.cs - Единственное, что я добавил здесь, это единственный экземпляр моего контекста в памяти

public void ConfigureServices(IServiceCollection services)
{
    //services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    services.AddMvc();
    services.AddSingleton<IItemRepository, ItemRepository>();
}

IItemRepository.cs - Мой интерфейс для DI

public interface IItemRepository
{
    List<ItemModel> Items { get; set; }
    void AddValue(ItemModel itemModel);
}

ItemRepository.cs - Конкретная реализация

public class ItemRepository : IItemRepository
{
    public List<ItemModel> Items { get; set; } = new List<ItemModel>();

    public ItemRepository()
    {
        Items.AddRange(
            new List<ItemModel> {
                new ItemModel {Id = 1, Name = "Test1" },
                new ItemModel {Id = 2, Name = "Test2" }
             }
        );
    }

    public void AddValue(ItemModel itemModel)
    {
        Items.Add(itemModel);
    }
}

ItemModel.cs - Мой класс модели для пользовательского ввода

public class ItemModel
{
    [Required]
    public int Id { get; set; }
    [MaxLength(10)]
    public string Name { get; set; }
}

ValuesController.cs

[Route("api/[controller]")]
[ApiController]
public class ValuesController : Controller
{
    private IItemRepository _context;

    public ValuesController(IItemRepository context)
    {
        _context = context;
    }

    // GET api/values
    [HttpGet]
    public ActionResult<IEnumerable<string>> Get()
    {
        return Ok(_context.Items);
    }

    // GET api/values/5
    [HttpGet("{id}", Name = "GetSingle")]
    public ActionResult<string> Get(int id)
    {
        return Ok(_context.Items.Where(x => x.Id == id));
    }

    // Problem here - placing a breakpoint in below method does not do anytthing as it will return a 400 bad request instead of 422
    [HttpPost]
    public ActionResult Post([FromBody] ItemModel itemModel)
    {
        if (!ModelState.IsValid)
        {
            return new UnprocessableEntityObjectResult(ModelState);
        }

        ItemModel addNew = new ItemModel { Id = itemModel.Id, Name = itemModel.Name };
        _context.AddValue(addNew);
        return Ok(addNew);
    }
}

Ответы [ 3 ]

0 голосов
/ 28 августа 2018

Ваша первая проблема может быть решена, если свойство обнуляется. Как прокомментировал Степен Мюке.

Также посмотрите здесь , возможно, атрибут BindRequired может помочь. В статье также описывается, как настроить поведение.

Для вашей второй проблемы, это новое (взлом) поведение Asp.Net Core 2.1. Новым является автоматический ответ 400 . Это объясняет, почему ваша точка останова не достигнута. Вы можете подавить это следующим образом:

services.Configure<ApiBehaviorOptions>(options =>
{
    options.SuppressModelStateInvalidFilter = true;
});
0 голосов
/ 08 октября 2018

Для вашей первой проблемы, если вы не хотите делать свойство обнуляемым, вы также можете поместить атрибут диапазона [Range (1, int.MaxValue)]], но 0 в этом случае не будет допустимым значением.

Для вашей второй проблемы, если вы все еще хотите автоматическую проверку модели из ApiControllerAttribute, но хотите получить код ответа 422 вместо 400, вы можете использовать опцию конфигурации InvalidModelStateResponseFactory.

services.Configure<ApiBehaviorOptions>(options => 
{
  options.InvalidModelStateResponseFactory = ctx => 
     new UnprocessableEntityObjectResult(ctx.ModelState);
});
0 голосов
/ 28 августа 2018

Если я отправляю запрос без идентификатора, создается идентификатор по умолчанию 0 обойти обязательный атрибут. Я предполагаю, что это C # функциональность для предоставления значений по умолчанию для полей. Есть ли способ обойти это?

Поскольку @ StephenMuecke ответил здесь , вам нужно изменить модель на

public class ItemModel
{
    [Required]
    public int? Id { get; set; }

    [MaxLength(10)]
    public string Name { get; set; }
}

Другая проблема заключается в том, что если я установлю точку останова в своем посте и отправить неверный запрос, он даже не входит в метод. Отправляет верните 400 неверных запросов, используя атрибуты проверки. Как эта работа? Останавливается ли запрос, как только он пытается привязать модель к неверное свойство (т. е. длина имени> 10)? Что мне нужно сделать, это отправить обратно необработанный объект 422 с тем же сообщением об ошибке вместо 400.

Это потому, что вы применили ApiControllerAttribute к контроллеру. Из документации :

Ошибки проверки автоматически вызывают ответ HTTP 400. Следующий код становится ненужным в ваших действиях:

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

Вы можете либо удалить атрибут, либо, как объясняется по той же ссылке, добавить его в конфигурацию запуска:

services.Configure<ApiBehaviorOptions>(options =>
{
    options.SuppressModelStateInvalidFilter = true;
})
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...