Web API模型设置器异常未通过

时间:2016-09-08 23:36:18

标签: c# asp.net-web-api exception-handling

我有一个APIController和一个输入类。

POST处理程序:

    public void Post([FromBody]NRCSSoilInput input)
    {
            new NRCSSoilWebService().SendRequest(input.box.west, input.box.north, input.box.east, input.box.south);
    }

输入类:

    public class NRCSSoilInput
    {
        public class BBox
        {
            public double north { get; set; }
            public double south { get; set; }
            public double east { get; set; }
            public double west { get; set; }
        }

        private BBox _box;
        public BBox box
        {
            get { return _box; }
            set { Validate(value); _box = value; }
        }

        public void Validate(BBox value)
        {
            if (value.west > value.east)
                throw new ArgumentOutOfRangeException("west", value.west, "West cannot be bigger than east coordinate.");

            ... etc ...
        }
    }

所发生的事情是,当遇到异常时,不会创建对象(如预期的那样),但是代码会继续并在Post中命中SendRequest,然后在input.box.west处作为输入失败。一片空白。这就是Post调用返回前端的内容。我希望它返回的是ArgumentOutOfRangeException,但我会停止代码。

ExceptionMessage:"Object reference not set to an instance of an object."
ExceptionType:"System.NullReferenceException"
Message:"An error has occurred."

我想一个简单的解决方案就是将验证移到帖子中,我不是那个想法的粉丝,尽管在我看来验证应该在创建Input对象时发生。

有几个侧面问题:

  • 将验证放入Post的另一个好处是我可以将它放入try catch块然后返回OK响应{status:error,message:"错误消息" },而不是500响应。

  • 输入json必须将east,west等作为整数,但它应该真正接受它们作为字符串然后转换它们。

有没有任何巧妙的方法来做上述事情?

1 个答案:

答案 0 :(得分:2)

最好的方法是通过数据注释(开箱即用或FluentValidations)和过滤器属性,如下所示。

第1步 - 构建自定义属性。 (我在本例中使用了开箱即用的数据注释)

[AttributeUsage(AttributeTargets.Property)]
public class DoubleGreaterThanAttribute : ValidationAttribute
{
    public DoubleGreaterThanAttribute(string doubleToCompareToFieldName)
    {
        DoubleToCompareToFieldName = doubleToCompareToFieldName;
    }

    private string DoubleToCompareToFieldName { get; set; }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        double west = (double)value;

        double east = (double)validationContext.ObjectType.GetProperty(DoubleToCompareToFieldName).GetValue(validationContext.ObjectInstance, null);

        if (east > west)
        {
            return ValidationResult.Success;
        }
        else
        {
            return new ValidationResult("West cannot be bigger than east coordinate.");
        }
    }
}

步骤-2使用自定义属性

注释属性
public class NRCSSoilInput
{
    public class BBox
    {
        public double north { get; set; }
        public double south { get; set; }
        [Required]
        public double east { get; set; }
        [Required]
        [DoubleGreaterThan("east")]
        public double west { get; set; }
    }
    public BBox box { get; set; }
}

步骤3-添加如下所示的过滤器属性类(最好在Filters文件夹中)

[AttributeUsage(AttributeTargets.Method, Inherited = true)]
public class ValidateModelAttribute : ActionFilterAttribute
{
    private readonly Func<Dictionary<string, object>, bool> _validate;

    public ValidateModelAttribute()
        : this(arguments =>
            arguments.ContainsValue(null))
    { }

    public ValidateModelAttribute(Func<Dictionary<string, object>, bool> checkCondition)
    {
        _validate = checkCondition;
    }

    public override void OnActionExecuting(HttpActionContext actionContext)
    {

        var modelState = actionContext.ModelState;

        if (!modelState.IsValid)
            actionContext.Response = actionContext.Request
                 .CreateErrorResponse(HttpStatusCode.BadRequest, modelState);

          }
}

步骤4 - 在控制器上注释过滤器属性

    [HttpPost]
    [ValidateModel]        
    public Void Post(NRCSSoilInput model)
    {
            return Ok();
    }

这样,只有当模型通过所有验证时,才会触发您的方法。希望这会有所帮助。