【问题标题】:Model normalization before model validation in Asp.Net Core 2.0+Asp.Net Core 2.0+ 中模型验证前的模型规范化
【发布时间】:2019-04-24 08:01:58
【问题描述】:

我正在使用automatic model validation(请参阅“更好的输入处理”)来保持我的控制器清洁;所以:

[HttpPost]
[ProducesResponseType(typeof(Product), 201)]
public IActionResult Post([FromBody] Product product)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }
    product = _repository.AddProduct(product);
    return CreatedAtAction(nameof(Get), new { id = product.Id }, product);
}

变成:

[HttpPost]
[ProducesResponseType(201)]
public ActionResult<Product> Post(Product product)
{
    _repository.AddProduct(product);
    return CreatedAtAction(nameof(Get), new { id = product.Id }, product);
}

但是,我确实有一些具有 phonenumber 属性的模型。我想在调用模型验证之前对这些进行“规范化”。我的意思是我想从各种输入中标准化这些属性(string 类型),例如:

  • +31 23 456 7890
  • (023) 4567890
  • 023 - 4567 890
  • ...

E.164 notation

  • +31234567890

因此,无论用户以何种形式输入电话号码,在调用验证之前,我都希望确保它始终采用 E.164 形式(“规范化”)。这种标准化是如何完成的是无关紧要的(如果你坚持,我使用libphonenumber)。作为第二个,也许不那么复杂的例子,我可以想象一个字符串在调用验证之前总是大写/小写。

在调用验证之前调用我的规范化过程的正确或最佳方法是什么?我需要写一些中间件吗?

同样相关:我的模型包含属性,因此规范器知道要规范化哪些属性(以及如何规范化):

class ExampleModel {

    public int Id { get; set; }

    public string Name { get; set; }

    [NormalizedNumber(NumberFormat.E164)]
    public string Phonenumber { get; set; }
}

我猜中间件(? 或任何解决方案) 然后可以采用模型,确定是否有任何属性(递归地)具有该属性并在需要时调用规范器.

【问题讨论】:

  • 我认为最早的可访问点将在自定义模型绑定器中。
  • 自定义模型绑定器将是处理此问题的最佳方式。看看这个dotnetcoretutorials.com/2016/12/28/…
  • 谢谢两位!我会看看自定义模型绑定器!
  • @Paresh 看起来很有希望;但是,我的模型可能非常复杂,我想调用原始模型绑定器绑定和调用我的“格式化程序”的 after。该链接未显示如何从自定义绑定器正确调用“原始”模型绑定器,因此我可以在 it 完成它的工作之后更改所有“标记”属性。你碰巧有一个很好的资源吗?
  • 我已经发布了一个follow up 问题,因为我无法让它按照我想要的方式工作。

标签: c# asp.net-web-api asp.net-web-api2 asp.net-core-2.1 model-validation


【解决方案1】:

也许您可以使用 Formatter 来使用类似的方法。我使用类似的方法在我的 API 中将所有传入日期转换为 UTC 格式

public class JsonModelFormatter : JsonMediaTypeFormatter
{
    public override System.Threading.Tasks.Task<Object> ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger, CancellationToken cancellationToken)
    {

        System.Threading.Tasks.Task<Object> baseTask = base.ReadFromStreamAsync(type, readStream, content, formatterLogger, cancellationToken);

        if (baseTask.Result != null)
        {
            var properties = baseTask.Result.GetType().GetProperties();
            foreach (var property in properties)
            {
                //Check Property attribute and decide if you need to format it
                if (property.CustomAttributes.Where (x=> you condition here))
                {
                    if (property.CanWrite && property.GetValue(baseTask.Result, null) != null)
                    {
                        var propValue = ((string)property.GetValue(baseTask.Result, null));
                       //Update propValue here 
                       property.SetValue(baseTask.Result, newPropValue);
                    }
                }
            }

        }
        return baseTask;
    }

    public override bool CanReadType(Type type)
    {
        return true;
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-02-17
    • 2017-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-09
    相关资源
    最近更新 更多