Web API - 检测空赋值

时间:2016-03-02 15:34:00

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

我试图想出一个可以检测属性何时设置为null的模式。类似Nullable<T>类的东西,但更高级。我们称之为MoreThanNullable<T>。基本上我需要根据以下3种情况做一些不同的事情:

  1. 该属性未设置
  2. 该属性设置为null
  3. 该属性设置为T的实例。
  4. 我创建了自己的类来实现这个&#34;实例化&#34;属性,它都在测试场景中工作。一些示例代码:

    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收到的财产之间的区别。 (注意:在我们的应用程序中,未设置和设置为null之间存在明显差异)

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

    ** 更新 **

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

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

    我似乎总是在我的问题中遗漏重要内容......

2 个答案:

答案 0 :(得分:2)

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

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

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

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

现在它们是对象并且反序列化。你的struct也有一个默认的空构造函数,如果它是一个带有私有默认构造函数的类,它将不再起作用。

现在,您如何获得像字符串这样的基本类型来反序列化到您的自定义类型?您可以使用自定义web api action filter执行此操作,并解析传入的json并将其映射到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());

答案 1 :(得分:1)

我认为你已经陷入了.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
}

在该逻辑中,您应该填写缺少信息的私人字段。 必须根据值是否为null来设置hasValue字段。 实例化应该有一个私有字段与setter私有。它应该建立在真正的公共价值观上。 如果hasValue为true,则实例化显然是正确的,否则怎么办? 所以问题是,我们怎样才能将hasvalue假,但是实例化为真?

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