Как улучшить ModelBinder в ASP.NET Core (обновить значения свойств для класса модели) - PullRequest
0 голосов
/ 22 января 2019

Я хотел бы улучшить конечный результат, который возвращает ModelBinder.
Например:

public class MyModel
{
    public int Order {get;set;}

    [MyUpperCaseAttribute]
    public string Title {get;set;}
}

В методе API я ожидаю, что все строковые свойства в MyModel с MyUpperCaseAttribute равныв верхнем регистре.

Например:

[HttpPost("AddRecord")]
public async Task<ActionResult<int>> AddRecord(MyModel model)
{
    model.Title should be upper case, even if send from client in lower case.
}

Моя идея состояла в том, чтобы переопределить значение по умолчанию ModelBinder и перечислить все свойства и проверить, является ли свойство строкой, имеет ли MyUpperCaseAttribute и правильное ли свойствозначение в верхнем регистре.Я проверяю документацию, но примеры не заполняются правильно, так как они полностью переделывают то, что возвращается.Я хотел бы просто изменить свойства результата.

Каков наилучший вариант для достижения желаемого поведения?

Важно: (отредактировано):
Было быхорошо, если атрибуты директивы могут быть сложены:

public class MyModel
{
    public int Order {get;set;}
    [MyUpperCaseAttribute]
    [RemoveSpacesAttribute]
    public string Title {get;set;}
}

Отредактировано:
Это похоже на this , но если нет, то это ASP.NETЯдро, а по ссылке это просто ASP.NET.Метод, свойства, интерфейсы ... не одно и то же.

Я должен сказать, что было бы неплохо, если бы работало правило JSON:

public class MyModel
{
    public int Order {get;set;}
    public string Title {get;set;}
}

должно работать, если {order: 1, title: "test"}(обратите внимание, строчные буквы) отправляется с JavaScript.

Ответы [ 3 ]

0 голосов
/ 22 января 2019

Возможно, это не лучший вариант, но я бы просто использовал .ToUpper() метод расширения вместо фильтра пользовательских атрибутов.

public class MyModel
{
    private string _title;
    public int Order {get;set;}

    public string Title { get => _title.ToUpper(); set => _title = value.ToUpper(); }
}
0 голосов
/ 16 февраля 2019

Здесь есть большая красная сельдь, и это тот факт, что кажется, что такого рода вещи можно и нужно достичь с помощью привязки модели.К сожалению, в ASP.Net Core Web API это не так: поскольку входящие данные представляют собой JSON, на самом деле они обрабатываются входными форматерами , а не связывателями моделей.Поэтому, чтобы достичь желаемого эффекта, вам необходимо создать собственный пользовательский форматировщик ввода, который заменяет стандартный JsonInputFormatter.

Первый атрибут:

[AttributeUsage(AttributeTargets.Property)]
public class ToUppercaseAttribute : Attribute
{
}

Затем мы украсим наш класс модели следующим образом:

public class MyModel
{
    public int Order { get; set; }

    [ToUppercase]
    public string Title { get; set; }
}

Теперь создайте наш пользовательский форматировщик ввода, который проверяет этот атрибут и преобразует выходные данные, если это необходимо.В этом случае он просто переносит и делегирует JsonInputFormatter для выполнения тяжелой работы в обычном режиме, а затем изменяет результат, если находит наш атрибут ToUppercaseAttribute в любом свойстве string:

public class ToUppercaseJsonInputFormatter : TextInputFormatter
{
    private readonly JsonInputFormatter _jsonInputFormatter;

    public ToUppercaseJsonInputFormatter(JsonInputFormatter jsonInputFormatter)
    {
        _jsonInputFormatter = jsonInputFormatter;

        foreach (var supportedEncoding in _jsonInputFormatter.SupportedEncodings)
            SupportedEncodings.Add(supportedEncoding);

        foreach (var supportedMediaType in _jsonInputFormatter.SupportedMediaTypes)
           SupportedMediaTypes.Add(supportedMediaType);
    }

    public override Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding)
    {
        var result = _jsonInputFormatter.ReadRequestBodyAsync(context, encoding);

        foreach (var property in context.ModelType.GetProperties().Where(p => p.PropertyType.IsAssignableFrom(typeof(string))
            && p.CustomAttributes.Any(a => a.AttributeType.IsAssignableFrom(typeof(ToUppercaseAttribute)))))
        {
            var value = (string)property.GetValue(result.Result.Model);
            property.SetValue(result.Result.Model, value.ToUpper());
        }

        return result;
    }
}

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

public static class MvcOptionsExtensions
{
    public static void UseToUppercaseJsonInputFormatter(this MvcOptions opts)
    {
        if (opts.InputFormatters.FirstOrDefault(f => f is JsonInputFormatter && !(f is JsonPatchInputFormatter)) is JsonInputFormatter jsonInputFormatter)
        {
            var jsonInputFormatterIndex = opts.InputFormatters.IndexOf(jsonInputFormatter);
            opts.InputFormatters[jsonInputFormatterIndex] = new ToUppercaseJsonInputFormatter(jsonInputFormatter);
        }
    }
}

И, наконец, вызываем этот метод для выполнения замены в Startup.cs:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services
            .AddMvc(options => options.UseToUppercaseJsonInputFormatter());
    }
}

Et voilà!

0 голосов
/ 22 января 2019

Вы можете сделать это внутри вашего MyUpperCaseAttribute следующим образом:

public class MyUpperCaseAttribute : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        if(value != null)
        {
            validationContext.ObjectType
            .GetProperty(validationContext.MemberName)
            .SetValue(validationContext.ObjectInstance, value.ToString().ToUpper(), null);
        }

        return null;
    }
}

Значение свойства будет преобразовано в UpperCase во время Model Binding. Я проверил его в своей стороне, и он отлично работает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...