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

Validating deserialised object with Postsharp Contracts

[这是一个明显的问题,但我找不到任何相关信息 - 如果有人可以参考我,那就太好了。]

WebAPI 项目中:

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

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

在编译期间,我猜想 PostSharp 的验证将它们自己放在属性的设置器中,所以当 obj 从请求的正文中反序列化时,它的字段没有被验证。

那么,best/clean 验证该对象的方法是什么?

干杯

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

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

这可以通过 PostSharp 方面来完成,但这肯定是非常重要的。另一种可能性是使用 reflection/expression 树来实现相同的目的。

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

正如 Daniel Balas 所写,没有简单的解决方案可以在反序列化对象后触发 PostSharp 的验证,除了实现 ISerializationCallback 接口的 OnDeserialized 方法。所以我 post 我写了一个方面,通过反射一个一个地深度复制 public 对象的属性,从而激活 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;
        }
    }