WebAPI实例化对象,即使它们在JSON中为空

时间:2016-01-05 21:33:20

标签: jquery json ajax asp.net-mvc asp.net-web-api

将JSON模型发布到WebAPI控制器方法时,我注意到如果JSON模型中有空对象,模型绑定器将实例化这些项,而不是将它们保留在服务器端对象中。

这与普通的MVC控制器如何绑定数据形成对比...如果对象在JSON中为空,它将实例化对象。

MVC控制器

public class HomeController : Controller
{
    [HttpPost]
    public ActionResult Test(Model model)
    {
        return Json(model);
    }
}

WebAPI控制器

public class APIController : ApiController
{
    [HttpPost]
    public Model Test(Model model)
    {
        return model;
    }
}

将获得POST的模型类

public class Model
{
    public int ID { get; set; }
    public Widget MyWidget { get; set; }
}

Model类中使用的类

public class Widget
{
    public int ID { get; set; }
    public string Name { get; set; }
}

当我将JSON模型发布到每个控制器时,以下是我的结果:

$.post('/Home/Test', { ID: 29, MyWidget: null })
//Results in: {"ID":29,"MyWidget":null}

$.post('/api/api/Test', { ID: 29, MyWidget: null })
//Results in: {"ID":29,"MyWidget":{"ID":0,"Name":null}}

如您所见,WebAPI方法使用对象实例化MyWidget属性,而MVC操作将其保留为null。

对我来说,WebAPI以这种方式运行似乎并不直观。它为什么要这样做?我可以在这方面使其表现得像MVC一样吗?

2 个答案:

答案 0 :(得分:3)

我认为它类似于之前在我们项目中遇到的问题。

您必须将jQuery的后置代码更改为以下代码:

$.ajax({
            type: 'POST',
            url: '/api/api/Test',
            data: JSON.stringify({ ID: 29, MyWidget: null }),
            contentType: "application/json",
            dataType: 'json',
            timeout: 30000
        })
        .done(function (data) {
        })
        .fail(function() {
        });

默认情况下,jQuery'发布'将参数作为form-url编码数据发送。

应用程序/ x-WWW窗体-urlencoded
ID 29
MyWidget

ID = 29&安培;进myWidget =

所以它绝对正确地反序列化了。 MyWidget是空字符串,因此它将具有Widget类的空值。

此外,我建议您为WebApi控制器添加Formatters配置:

public static void Register(HttpConfiguration config)
    {
        // Web API configuration and services

        // Web API routes
        config.MapHttpAttributeRoutes();

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );

        // Formatters
        JsonMediaTypeFormatter json = config.Formatters.JsonFormatter;

        config.Formatters.Clear();
        config.Formatters.Add(json);
    }

因此,您将仅使用JSON格式化程序进行API调用。

<强>更新

传递给MVC控制器的form-url编码数据的主要区别将由运行时处理,最后由DefaultModelBinder处理(如果自定义绑定器不可用)。因此,编码为form-url的数据通常用于MVC,因为通常数据是由HTML表单发布的。但Web API并不依赖于任何特定的编码设计。因此它使用特定的机制(格式化程序)来解析数据...例如json就像它在上面一样。 System.Net.Http.Formatting中的FormUrlEncodedMediaTypeFormatter和System.Web.Mvc中的DefaultModelBinder处理空字符串的方式不同。

对于DefaultModelBinder,空字符串将转换为null。 分析代码我可以决定BindModel方法首先创建空模型:

 if (model == null)
 {
     model = CreateModel(controllerContext, bindingContext, modelType);
 }

之后它将填充属性:

// call into the property's model binder
        IModelBinder propertyBinder = Binders.GetBinder(propertyDescriptor.PropertyType);
        object originalPropertyValue = propertyDescriptor.GetValue(bindingContext.Model);
        ModelMetadata propertyMetadata = bindingContext.PropertyMetadata[propertyDescriptor.Name];
        propertyMetadata.Model = originalPropertyValue;
        ModelBindingContext innerBindingContext = new ModelBindingContext()
        {
            ModelMetadata = propertyMetadata,
            ModelName = fullPropertyKey,
            ModelState = bindingContext.ModelState,
            ValueProvider = bindingContext.ValueProvider
        };
        object newPropertyValue = GetPropertyValue(controllerContext, innerBindingContext, propertyDescriptor, propertyBinder);

最后,GetBinder将为Widget类型(属性类型)返回fallbackBinder。并且fallbackBinder本身将调用ConvertSimpleType,其中字符串被处理如下:

        string valueAsString = value as string;
        if (valueAsString != null && String.IsNullOrWhiteSpace(valueAsString))
        {
            return null;
        }

我想没有任何标准描述从url编码的字符串到C#对象的转换。所以我不知道哪一个是正确的。无论如何,我确信你需要通过AJAX调用传递json而不是form-url编码数据。

答案 1 :(得分:0)

使用newtonsoft序列化程序。 Newtonsoft.Json将它们保持为空

using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;


public static class WebApiConfig
        {
            public static void Register(HttpConfiguration config)
            {
              var jsonformatter = new JsonMediaTypeFormatter();
              config.Formatters.Clear();
              config.Formatters.Add(jsonformatter);
         }
    }