简体   繁体   English

如何在ASP.NET Core中增强ModelBinder(更新模型类的属性值)

[英]How can I enhance ModelBinder in ASP.NET Core (update property values for a model class)

I would like to enhance final result that ModelBinder returns. 我想增强ModelBinder返回的最终结果。
For example: 例如:

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

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

In API method I expect that all string properties in MyModel which has MyUpperCaseAttribute is in upper case. 在API方法中,我希望MyModel中具有MyUpperCaseAttribute所有字符串属性都是大写的。

For example: 例如:

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

My idea was to override default ModelBinder and enumerate through all properties and check if property is string and has MyUpperCaseAttribute and correct property value to upper case. 我的想法是覆盖默认的ModelBinder并枚举所有属性,并检查属性是否为字符串并且具有MyUpperCaseAttribute并将属性值更正为大写。 I check documentation, but doesn't examples doesn't fill right, since they completely redesign what is returned. 我检查文档,但是没有示例没有正确填写,因为它们完全重新设计了返回的内容。 I would like to just modify result properties. 我想修改结果属性。

What would be the best option to achieve desired behaviour? 什么是实现理想行为的最佳选择?

Important: (edited): 重要:(已编辑):
It would be nice if directive attributes could be stacked: 如果可以堆叠指令属性,那将是很好的:

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

Edited: 编辑:
It looks similar to this , but if not other, this is ASP.NET Core, and on link is just ASP.NET. 它看起来与类似,但如果不是其他,则这是ASP.NET Core,而链接上只是ASP.NET。 Method, properties, interfaces... are not the same. 方法,属性,接口......不一样。

I should say, that it would be nice if JSON case rule would work: 我应该说,如果JSON案例规则可行,那将是很好的:

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

should work if {order: 1, title: "test"} (notice lowercase) is send from JavaScript. 如果从JavaScript发送{order: 1, title: "test"} (通知小写),则应该有效。

This might not be the 'best' option, but I would just use .ToUpper() extension method instead of a custom attribute filter. 这可能不是“最佳”选项,但我只使用.ToUpper()扩展方法而不是自定义属性过滤器。

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

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

You can do this thing inside your MyUpperCaseAttribute as follows: 您可以在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;
    }
}

Property value will be converted to UpperCase during Model Binding . Model Binding期间,属性值将转换为UpperCase I have checked it in my side and it works perfectly. 我已经在我身边检查了它,它完美无缺。

There's a big red herring here, and that's the fact that it appears that this is the sort of thing that could and should be accomplished via model binding. 这里有一个大红鲱鱼,这就是看起来这是可以而且应该通过模型绑定完成的事情。 Unfortunately, that's not the case in ASP.Net Core Web API: because the incoming data is JSON, it is in fact handled by input formatters , not model binders. 不幸的是,在ASP.Net Core Web API中并非如此:因为传入的数据是JSON,它实际上是由输入格式化程序处理的,而不是模型绑定程序。 Therefore, in order to achieve the desired effect, you need to create your own custom input formatter that replaces the standard JsonInputFormatter . 因此,为了达到预期的效果,您需要创建自己的自定义输入格式化程序来替换标准的JsonInputFormatter

First the attribute: 首先是属性:

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

Then we decorate our model class with it: 然后我们用它装饰我们的模型类:

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

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

Now create our custom input formatter that checks for that attribute and transforms the output if necessary. 现在创建我们的自定义输入格式化程序,检查该属性并在必要时转换输出。 In this case, it simply wraps and delegates to JsonInputFormatter to do the heavy lifting as normal, then modifies the result if it finds our ToUppercaseAttribute attribute on any string property: 在这种情况下,它只是包装并委托给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;
    }
}

Next we create an extension method that makes it simple to substitute the default JsonInputFormatter with our custom formatter: 接下来,我们创建一个扩展方法,使用我们的自定义格式化程序替换默认的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);
        }
    }
}

And finally, call that method to effect the replacement in Startup.cs : 最后,调用该方法来实现Startup.cs的替换:

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

Et voilà! Etvoilà!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM