使用Postsharp Contracts验证反序列化对象

时间:2015-01-07 11:19:43

标签: validation asp.net-web-api postsharp

[这是一个显而易见的问题,但我找不到任何关于它的东西 - 如果有人可以引用我,它会很棒。]

WebAPI项目中

public class MyObject
{
   [PostSharp.Patterns.Contract.Required]
   public string Name {get;set;}
}

public class MyController : ApiController
{
   public HttpResponseMessage Post([FromBody]MyObject obj)
   {
      /// ...
   }
}

在编译期间,我猜PostSharp的验证将自己置于属性的setter中,因此当从请求的主体反序列化obj时,其字段不会被验证。

那么,验证该对象的最佳/干净方式是什么?

干杯

2 个答案:

答案 0 :(得分:0)

目前还没有干净的方法来实现这样的验证,因为假定曾经序列化的对象已经有效。

为了强制验证逻辑,需要使用ISerializationCallback接口的OnDeserialized方法,遍历属性并强制将它们设置为当前值以强制执行验证。

这可以通过PostSharp方面来完成,但它肯定是非平凡的。其他可能性是使用反射/表达树来实现相同的目标。

如果您认为这是PostSharp的一个不错的功能,您可以投票PostSharp's UserVoice page

答案 1 :(得分:0)

Daniel Balas写道,除了实现OnDeserialized接口的ISerializationCallback方法之外,没有简单的解决方案可以在反序列化对象后触发PostSharp的验证。所以我发布了一个我写的Aspect,它通过反射逐个深度复制对象的公共属性,从而激活setter中的验证。

    [Serializable]
    public sealed class ArgsValidationAspect : MethodInterceptionAspect
    {
        public override bool CompileTimeValidate(MethodBase method)
        {
            if (!method.GetParameters().Any(p => p.ParameterType.IsClass))
            {
                Message.Write(method, SeverityType.Error, "MY001", "Cannot apply HttpObjectValidationAspect to method '{0}'.", method);
                return false;
            }

            return true;
        }


        public override void OnInvoke(MethodInterceptionArgs args)
        {
            foreach (var arg in args.Arguments)
            {
                try
                {
                    RecursiveCopyInstance(arg);
                }
                catch (Exception e)
                {
                    throw e.InnerException ?? e;
                }
            }

            base.OnInvoke(args);
        }

        private static object RecursiveCopyInstance(object origin)
        {
            var type = origin.GetType();
            var instance = Activator.CreateInstance(type);

            foreach (var prop in type.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance))
            {
                var val = prop.GetValue(origin);
                if (val != null && !prop.PropertyType.IsPrimitive && !prop.PropertyType.Equals(typeof(string)))
                {
                    val = RecursiveCopyInstance(val);
                }

                prop.SetValue(instance, val);
            }

            return instance;
        }
    }