Web API - 检测空赋值

Web API - Detecting null assignment

我正在尝试提出一种模式,可以检测 属性 何时设置为空。类似于 Nullable<T> class,但更高级一些。我们称它为 MoreThanNullable<T>。基本上我需要根据以下 3 种情况做一些不同的事情:

  1. 从未设置 属性
  2. 属性 被设置为 null
  3. 属性 设置为 T 的一个实例。

我创建了自己的 class 来使用 "instantiated" 属性 来执行此操作,并且它都在测试场景中运行。一些示例代码:

public struct MoreThanNullable<T>
{
    private bool hasValue;
    internal T value;
    public bool Instantiated { get; set; }

    public MoreThanNullable(T value)
    {
        this.value = value;
        this.hasValue = true;
        Instantiated = true;
    }
// ... etc etc...

测试如我所料通过:

[TestFixture]
public class MoreThanNullableTests
{
    public class AccountViewModel
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
        public MoreThanNullable<string> Test1 { get; set; }
        public MoreThanNullable<string> Test2 { get; set; }
        public MoreThanNullable<string> Test3 { get; set; }
    }

    [Test]
    public void Tests()
    {
        var myClass = new AccountViewModel();
        Assert.AreEqual(false, myClass.Test1.Instantiated);
        myClass.Test1 = null;
        Assert.AreEqual(true, myClass.Test1.Instantiated);
    }
}

接下来,使用相同的视图模型,我将其连接到我的 REST 服务上的 POST,并传入以下 JSON:

{
    Name:"test",
    Test1:null,
    Test2:"test"
}

...一切都崩溃了。 Test1 和 Test3 都没有被实例化!显然,我不希望 Test3 被实例化,但 Test1 保持未实例化是出乎意料的。实际上,我无法区分我通过 REST 收到和未收到的 属性 之间的区别。 (注意:在我们的应用程序中,not set 和 set to null 有明显的区别)

我是不是做错了什么?或者这是 Web 的正确行为 API?

** 更新 **

我可能没有说清楚的是,我已经有效地复制了 Nullable<T> class 来实现这一点,包括像这样重载隐式运算符:

public static implicit operator MoreThanNullable<T>(T value)
{
    return new MoreThanNullable<T>(value);
}

我的问题似乎总是遗漏重要的东西...

我认为您遇到了 .net 中服务反序列化的常见问题

我一直在 WCF 中看到这个问题,但我认为它也存在于 json 反序列化中

https://msdn.microsoft.com/en-us/library/system.runtime.serialization.ondeserializedattribute(v=vs.110).aspx

.NET在反序列化过程中不执行构造函数,所以如果你需要在反序列化后执行一些代码你必须添加一些代码作为这些

[OnDeserialized()]
internal void OnDeserializedMethod(StreamingContext context) 
{
   // ... logic here after deserialization
}

按照该逻辑,您应该填写缺少信息的私有字段。 hasValue 字段必须根据值是否为 null 进行设置。 实例化应该有一个私有字段 setter private。它应该在 public setter 的值上设置为真。 如果 hasValue 为真,则实例化显然为真,否则怎么可能呢? 所以问题是,我们如何才能让 hasvalue 为 false 而 instatiated 为 true?

这是唯一无法用其他值确定该值的情况,因此我们需要其他东西。制作实例化 public 的 setter 是最简单的方法,但为了确保对象一致性,您应该在反序列化后检查它以确保以前的对象规则...

为什么不起作用?在你的 JSON

{
    Name:"test",
    Test1:null,
    Test2:"test"
}

Test1 end Test2 是原始类型,但您创建了自己的类型,这些是它应该映射到的属性,因此它不再是原始类型,而是一个复杂的对象。所以实际上你的 JSON 应该也包含对象。如果您将其更改为

,它将起作用
{
    Name:"test",
    Test1:{Instantiated:false, value: null},
    Test2:{Instantiated:false, value: "test"},
}

因为现在它们是对象并被反序列化。此外,您的结构有一个默认的空构造函数,如果它是一个带有私有默认构造函数的 class,它将无法再次工作。

现在,您如何获得像字符串这样的原始类型以反序列化为您的自定义类型?您也许可以使用自定义 web api action filter and parse the incoming json and map it to your object in the OnActionExecuting.

来执行此操作

编辑 2

假设您正在使用 JSON.NET from newtonsoft,您可以插入自定义 JSON 转换器。这将是整个 Web api 应用程序的全局更改,不需要自定义过滤器。

public sealed class MoreThanNullableConverter : Newtonsoft.Json.JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeof (MoreThanNullable<>);
    }
    public override bool CanWrite { get { return false; } }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        // return a new MoreThanNullable instance with value
        return Activator.CreateInstance(objectType, reader.Value);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

改为webapiconfig.cs

var formatters = GlobalConfiguration.Configuration.Formatters;
var jsonFormatter = formatters.JsonFormatter;
var settings = jsonFormatter.SerializerSettings;
jsonFormatter.SerializerSettings.Converters.Add(new MoreThanNullableConverter());