未包含在JSON

时间:2016-01-14 09:34:36

标签: c# asp.net json asp.net-web-api asp.net-web-api2

我有一个模型,我使用DataAnnotations来执行验证,例如

public class OrderDTO
{
    [Required]
    public int Id { get; set; }

    [Required]
    public Decimal Amount { get; set; }
}

然后我在每个请求中检查ModelState以确保JSON有效。

但是,我遇到上面Amount等号码属性的问题。即使将其设置为[Required],如果它未包含在JSON中,它将跳过ModelState验证,因为它会自动默认为0而不是null,因此模型看起来有效即使它不是。

一种简单的方法来修复'这是将所有数字属性设置为可为空(int?Decimal?)。如果我这样做,默认为0并不会发生,但我不喜欢这个作为一个明确的解决方案,因为我需要改变我的模型。

如果属性不属于JSON,有没有办法将属性设置为null

4 个答案:

答案 0 :(得分:5)

因为Decimal是不可为空的类型,所以你不能这样做。 您需要Decimal?来绑定null值。

答案 1 :(得分:3)

您必须使用可空类型。由于非可空值(如您所知)不能为null,因此它将使用0作为默认值,因此看起来具有值并始终通过验证。

正如您所说,验证工作必须为null,因此可以为空。另一种选择可能是编写自己的验证属性,但这可能会导致问题,因为您很可能会说如果为null或0则无效,当您希望将0作为可接受的值时,这是一个很大的问题,因为您需要另一种决定0是否有效的方法。

自定义验证的示例,并非特定于此情况。 Web API custom validation to check string against list of approved values

另一个选项可能是添加另一个可为空的属性并将值提供给非可空属性。同样,这可能会导致0值出现问题。以下是Id属性的示例,您的json现在需要发送NullableId而不是Id。

public class OrderDTO
{
    //Nullable property for json and validation 
    [Required]
    public int? NullableId {
        get {
            return Id == 0 ? null : Id; //This will always return null if Id is 0, this can be a problem
        }
        set {
            Id = value ?? 0; //This means Id is 0 when this is null, another problem
        }
    }

    //This can be used as before at any level between API and the database
    public int Id { get; set; }
}

正如您所说的另一种选择是通过整个堆栈将模型更改为可空值。

最后,你可以看一下外部模型进入具有可空属性的api,然后手动或使用类似AutoMapper之类的东西将其映射到当前模型。

答案 2 :(得分:1)

我同意其他人认为Decimal是非Nullable类型不能赋值为null。此外,Required属性仅检查null,空字符串和空格。因此,根据您的特定要求,您可以使用CustomValidationAttribute,并且可以创建自定义验证类型来执行" 0"检查十进制属性。

答案 3 :(得分:1)

user = yield users.findOne({ 'profile.personal.email': email, 'profile.company.email': email }); int无法Decimal。这就是为什么那些无法创造的。

你有几个选项[编辑:我刚刚意识到你特别要求使用Web-API,在这种情况下我相信自定义绑定选项会比我发布的代码更复杂。]:

  • 使字段在DTO
  • 中可为空
  • 创建具有可空类型的null,在视图模型上添加所需的验证属性,并将此ViewModel映射到您的DTO(可能使用automapper或类似的东西)。 <击>
  • <击>
  • 手动验证请求(容易出错并且容易出错)

    ViewModel

    <击>

  • 创建custom binder并在那里进行验证:

    public ActionResult MyAction(OrderDTO order)
    {
       // Validate your fields against your possible sources (Request.Form,QueryString, etc)
       if(HttpContext.Current.Request.Form["Ammount"] == null)
       {
           throw new YourCustomExceptionForValidationErrors("Ammount was not sent");
       }
    
       // Do your stuff
    }
    

    使用以下答案中描述的技巧之一将您的活页夹注册到您的模型:https://stackoverflow.com/a/13749124/149885 例如:

    public class OrderModelBinder : DefaultModelBinder 
    {
       protected override bool OnPropertyValidating(ControllerContext controllerContext, ModelBindingContext bindingContext,
    PropertyDescriptor propertyDescriptor, object value)
       {
           if ((propertyDescriptor.PropertyType == typeof(DateTime) && value == null) ||
              (propertyDescriptor.PropertyType == typeof(int) && value == null) ||
              (propertyDescriptor.PropertyType == typeof(decimal) && value == null) ||
              (propertyDescriptor.PropertyType == typeof(bool) && value == null))
           {
               var modelName = string.IsNullOrEmpty(bindingContext.ModelName) ? "" : bindingContext.ModelName + ".";
               var name = modelName + propertyDescriptor.Name;
               bindingContext.ModelState.AddModelError(name, General.RequiredField);
            }
    
        return base.OnPropertyValidating(controllerContext, bindingContext, propertyDescriptor, value);
        }
    }