【问题标题】:How can I enhance ModelBinder in ASP.NET Core (update property values for a model class)如何在 ASP.NET Core 中增强 ModelBinder(更新模型类的属性值)
【发布时间】:2019-01-22 12:33:53
【问题描述】:

我想增强 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 Core,而链接上只是 ASP.NET。方法、属性、接口……不一样。

我应该说,如果 JSON 大小写规则有效,那就太好了:

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

如果 {order: 1, title: "test"}(注意小写)是从 JavaScript 发送的,应该可以工作。

【问题讨论】:

  • @IanKemp 不是。它谈论的是 ASP.NET Core,而不是 ASP.NET。有不同的方法、名称、接口、原理、实现......
  • 有大量关于如何为 ASP.NET Core 编写模型绑定器的资源,您当然应该能够将其中之一与我链接到的答案结合起来以实现您的需求吗?跨度>
  • 下划线字符串模型绑定问题讨论如何绑定一个属性。我正在搜索如何处理整个模型(更改属性值)。
  • 是的,我就是这个意思。在这种情况下,反序列化将使用 JSON.NET。您可能能够在 JSON.NET 本身中找到一些东西,您可以对其进行配置以执行您想要的操作。

标签: c# asp.net-core asp.net-core-2.0


【解决方案1】:

这可能不是“最佳”选项,但我会使用 .ToUpper() 扩展方法而不是自定义属性过滤器。

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

    public string Title { get => _title.ToUpper(); set => _title = value.ToUpper(); }
}

【讨论】:

  • 这是一个想法,但属性会好很多(它更短,更透明,应该开箱即用)。
【解决方案2】:

您可以在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;
    }
}

属性值将在Model Binding 期间转换为UpperCase。我已经在我身边检查过它,它工作得很好。

【讨论】:

  • 我想避免这种情况。现在我有近 100 个不同的 API 调用,我想避免在每个 API 方法中编写这段代码。另外,如果我在 2 种不同的 API 方法中使用相同的 Model 会怎样。我需要在这两种方法中编写这段代码。 ModelBinder 只有一个。
  • 但我仍然需要在每个 API 方法中调用辅助方法。我想避免这种情况。开发人员应该定义模型类,在一些代码上应该照顾一切。就像Required 属性一样。您不必在每个 API 方法中编写代码来检查是否设置了所有 Required 属性。
  • 我确信它可以自动完成。必需,MaxLength ...所有模型验证都可以开箱即用,这也应该。我认为这是有道理的。开发人员只需要编辑模型类。没有别的了。
  • 这个答案已经完成了一半,只需要打包成一个自定义的ModelBinder。我已经链接到一个类似的问题,它可以完成整个事情,希望这就是你正在寻找的。​​span>
  • 嗨@TanvirArjel 我只是检查JsonContractResolver 和this,因为在验证模拟中更新模型有点奇怪。等我研究完再回来。
【解决方案3】:

这里有一个大红鲱鱼,这就是事实,这似乎是可以而且应该通过模型绑定来完成的事情。不幸的是,在 ASP.Net Core Web API 中并非如此:因为传入的数据是 JSON,它实际上是由 input formatters 处理的,而不是模型绑定器。因此,为了达到预期的效果,您需要创建自己的自定义输入格式化程序来替代标准的JsonInputFormatter

首先是属性:

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

然后我们用它装饰我们的模型类:

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

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

现在创建我们的自定义输入格式化程序,检查该属性并在必要时转换输出。在这种情况下,它只是简单地包装并委托给JsonInputFormatter 以照常完成繁重的工作,然后如果它在任何string 属性上找到我们的ToUppercaseAttribute 属性,则修改结果:

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());
    }
}

等等!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多