Различение явного «ноль» и «не указано» в ASP.NET Core ApiController - PullRequest
5 голосов
/ 24 сентября 2019

Это мой самый первый вопрос после многих лет здесь, поэтому я надеюсь, что не нарушу никаких правил.

В некоторых из методов POST моего ASP.NET Core API я бы хотелразрешить клиентам предоставлять только те свойства, которые они хотят обновить, в теле своего запроса POST.

Вот упрощенная версия моего кода:

[Route("api/v{version:apiVersion}/[controller]")]
[ApiController]
public sealed class FooController : ControllerBase
{
    public async Task<IActionResult> UpdateFooAsync(Guid fooGuid, [FromBody]UpdateFooModel model)
    {
        ... Apply updates for specified properties, checking for authorization where needed...

        return Ok();
    }
}

public sealed class UpdateFooModel
{
    [BindProperty] public int? MaxFoo { get; set; }
    [BindProperty] public int? MaxBar { get; set; }
}

public sealed class Foo
{
    public int? MaxFoo { get; set; }
    public int? MaxBar { get; set; }
}

MaxBar и MaxFoo обаобнуляемые целочисленные значения, где нулевое значение означает, что максимума нет.

Я пытаюсь разрешить клиентам отправлять, например, следующую конечную точку:

  • Установка MaxBar на ноль и установка MaxFoo на 10

    {
        "maxBar": null,
        "maxFoo": 10
    }
    
  • Установка MaxBar на ноль, не касаясь MaxFoo

    { "maxBar": null }
    
  • Обновить MaxBarдо 5, не касаясь MaxFoo

    { "maxBar": 5 }
    

В моем методе UpdateFooAsync я хочу обновить только свойства, указанные в запросе.

Однако, когдапроисходит связывание модели, задаются неопределенные свойстваt к их значениям по умолчанию (null для типов, допускающих значение NULL).

Каков наилучший способ выяснить, было ли значение явно установлено в null (оно должно быть установлено в null),или просто не присутствовал в запросе (он не должен обновляться)?

Я пытался проверить ModelState, но он не содержал ключей для «модели», только для напечатанного Guidпараметр.

Любой другой способ решения основной проблемы также приветствуется.

Спасибо!

Ответы [ 2 ]

0 голосов
/ 25 сентября 2019

Отвечая на мой собственный вопрос здесь, основываясь на предложении @ russ-w (спасибо!): Пометив свойство bool в установщике каждого необязательного свойства, мы можем узнать, предоставлено оно или нет.

public sealed class UpdateFooModel
{
    private int? _maxFoo;
    private int? _maxBar;

    [BindProperty] 
    public int? MaxFoo
    { 
        get => _maxFoo;
        set
        {
            _maxFoo = value;
            MaxFooSet = true;
        }
    }

    public bool MaxFooSet { get; private set; }

    [BindProperty] 
    public int? MaxBar
    { 
        get => _maxBar;
        set
        {
            _maxBar = value;
            MaxBarSet = true;
        }
    }

    public bool MaxBarSet { get; private set; }
}

Дальнейшие улучшения или другие решения по-прежнему приветствуются, конечно!

0 голосов
/ 24 сентября 2019

Вот потенциальное решение.Используйте логику в установщиках класса UpdateFooModel, чтобы проверить наличие нулевого значения и назначить другое значение, например Int32.MaxValue.Сеттеры вызываются только при передаче параметра. В примере, если null передается явно, он будет преобразован в Int32.MaxValue.Если параметр не указан, значение останется нулевым.Альтернативой использованию сеттеров было бы использование конструктора по умолчанию и добавление некоторой логики для установки различных значений в зависимости от того, были ли указаны параметры.Пример:

public sealed class UpdateFooModel
{
    private int? _maxFoo;
    public int? MaxFoo
    {
        get
        {
            return _maxFoo;
        }

        set
        {
            _maxFoo = (value == null) ? Int32.MaxValue : value;
        }
    }

    private int? _maxBar;
    public int? MaxBar
    {
        get
        {
            return _maxBar;
        }

        set
        {
            _maxBar = (value == null) ? Int32.MaxValue : value;
        }
    }
}
...