jquery - WebAPI 实例化对象,即使它们在 JSON 中为 null

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

当将 JSON 模型发布到 WebAPI Controller 方法时,我注意到,如果 JSON 模型中存在空对象,模型绑定(bind)器将实例化这些项目,而不是将它们保留在服务器端对象中为空。

这与普通 MVC Controller 绑定(bind)数据的方式相反...如果 JSON 中的对象为 null,它不会实例化对象。

MVC Controller

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

WebAPI Controller

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

将发布的模型类

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

模型类中使用的类

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

以下是我将 JSON 模型发布到每个 Controller 时的结果:

$.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 操作将其保留为空。

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

最佳答案

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

您必须将 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“posts”将参数作为表单 URL 编码数据发送。

application/x-www-form-urlencoded
ID 29
我的小部件

ID=29&MyWidget=

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

此外,我建议您为 WebApi Controller 添加格式化程序配置:

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 Controller 的表单 url 编码数据将由运行时处理,并最终由 DefaultModelBinder 处理(如果自定义绑定(bind)器不可用)。因此,编码为 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,而不是表单 url 编码的数据。

关于jquery - WebAPI 实例化对象,即使它们在 JSON 中为 null,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34621608/

相关文章:

python - 给定对象的 JSON.dump 在命令行中工作,而不是在脚本内工作

ajax - 从 JQuery 向 Web API 进行 POST 时出现问题

jquery - 使用 Ajax 时的字符集编码? jQuery

ruby-on-rails - Rails : Completed 500 Internal Server Error, 与 AngularJS $http.post 调用

javascript - HTML 如何相对于两个 div 定位一个按钮?

php - Ajax不调用回调函数

javascript - 向收件箱选项卡和垃圾选项卡添加不同的下拉菜单

Jquery Mobile 出现长选择多个选项和数据 native 菜单 ="false"的问题

javascript - 正确的 JSON 格式

ios - 如何测试 Json 响应是否包含错误或正确的 json