WebApi未能兑现JsonObjectAttribute设置

时间:2014-07-29 23:02:32

标签: c# asp.net-mvc-4 asp.net-web-api json.net deserialization

所以我有一个ApiController ......

public class MetaDataController : ApiController
{
    [HttpPost]
    public HttpResponseMessage Test(TestModel model)
    {
        //Do Stuff
        return  new HttpResponseMessage(HttpStatusCode.OK);
    }
}

接受模特......

[JsonObject(ItemRequired = Required.Always)]
public class TestModel
{
    public int Id { get; set; }
    public IEnumerable<SubModel> List { get; set; } 
}

public class SubModel
{
    public int Id { get; set; }
}

以Json的形式......

{  "Id": 1,  "List": [{ "Id": 11 }, { "Id": 12 } ] }

当发布到此控制器操作时,TestModel上的属性应该使Json.Net在Json缺少属性时抛出JsonSerializationException。我编写单元测试来确保这种行为按预期工作。

[Test]
public void Test()
{
    var goodJson = @"{ 'Id': 1, 
                        'List': [ {'Id': 11}, {'Id': 12} ]
                        }";

    Assert.DoesNotThrow(() => JsonConvert.DeserializeObject<TestModel>(goodJson));

    var badJson = @"{ 'Id': 1 }";

    Assert.That(()=>JsonConvert.DeserializeObject<TestModel>(badJson),
        Throws.InstanceOf<JsonSerializationException>().
        And.Message.Contains("Required property 'List' not found in JSON."));
}

当将形式良好的Json发布到controlelr动作时,一切正常。但是如果json缺少必需的属性,则不会抛出任何异常。映射到缺少的属性的TestModel成员为null。

为什么JsonConvert按预期工作,但是通过WebApiController的自动Json反序列化无法遵守TestModel上的属性?

1 个答案:

答案 0 :(得分:1)

对于咯咯笑,我决定确定我的应用程序使用Json.Net进行json反序列化。所以我写了一个MediaTypeFormatter

public class JsonTextFormatter : MediaTypeFormatter
{
    public readonly JsonSerializerSettings JsonSerializerSettings;
    private readonly UTF8Encoding _encoding;

    public JsonTextFormatter(JsonSerializerSettings jsonSerializerSettings = null)
    {
        JsonSerializerSettings = jsonSerializerSettings ?? new JsonSerializerSettings();

        SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/json"));
        _encoding = new UTF8Encoding(false, true);
        SupportedEncodings.Add(_encoding);
    }

    public override bool CanReadType(Type type)
    {
        if (type == null)
        {
            throw new ArgumentNullException();
        }

        return true;
    }

    public override bool CanWriteType(Type type)
    {
        return true;
    }

    public override Task<object> ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger)
    {
        var serializer = JsonSerializer.Create(JsonSerializerSettings);

        return Task.Factory.StartNew(() =>
        {
            using (var streamReader = new StreamReader(readStream, _encoding))
            {
                using (var jsonTextReader = new JsonTextReader(streamReader))
                {
                    return serializer.Deserialize(jsonTextReader, type);
                }
            }
        });
    }

    public override Task WriteToStreamAsync(Type type, Object value, Stream writeStream, HttpContent content, TransportContext transportContext)
    {
        var serializer = JsonSerializer.Create(JsonSerializerSettings);
        return Task.Factory.StartNew(() =>
        {
            using (
                var jsonTextWriter = new JsonTextWriter(new StreamWriter(writeStream, _encoding))
                {
                    CloseOutput = false
                })
            {
                serializer.Serialize(jsonTextWriter, value);
                jsonTextWriter.Flush();
            }
        });
    }
}

并修改了我的WebApiConfig以使用它而不是默认值。

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.Filters.Add(new HandleSerializationErrorAttribute());

        config.Formatters.RemoveAt(0);
        var serializerSettings = new JsonSerializerSettings
        {
            MissingMemberHandling = MissingMemberHandling.Error
        };
        config.Formatters.Insert(0, new JsonTextFormatter(serializerSettings));

    }
}

我还添加了一个ExceptionFilterAttribute来捕获序列化错误并返回有关错误的相关信息。

public class HandleSerializationErrorAttribute : ExceptionFilterAttribute
{
   public override void OnException(HttpActionExecutedContext context)
    {
        if (context.Exception is JsonSerializationException)
        {
            var responseMessage = new HttpResponseMessage(HttpStatusCode.BadRequest);
            responseMessage.Content = new StringContent(JsonConvert.SerializeObject(context.Exception.Message));

            context.Response = responseMessage;
        }
    }
}

所以它是:.net MVC 4 WebApi SAYS它使用Json.Net,但默认的JsonFormatter拒绝服从装饰我的模型的Json属性。手动显式设置格式化程序可以解决问题。