当将 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/