将JSON模型发布到WebAPI控制器方法时,我注意到如果JSON模型中有空对象,模型绑定器将实例化这些项,而不是在服务器端对象中保留它们.
这与普通MVC控制器如何绑定数据形成对比……如果对象在JSON中为null,则不会实例化对象.
MVC控制器
@H_301_10@public class HomeController : Controller { [HttpPost] public ActionResult Test(Model model) { return Json(model); } }
WebAPI控制器
@H_301_10@public class APIController : ApiController { [HttpPost] public Model Test(Model model) { return model; } }
将获得POST的模型类
@H_301_10@public class Model { public int ID { get; set; } public Widget MyWidget { get; set; } }
Model类中使用的类
@H_301_10@public class Widget { public int ID { get; set; } public string Name { get; set; } }
当我将JSON模型发布到每个控制器时,这是我的结果:
@H_301_10@$.post('/Home/Test',{ ID: 29,MyWidget: null }) //Results in: {"ID":29,"MyWidget":null} $.post('/api/api/Test',"MyWidget":{"ID":0,"Name":null}}
如您所见,WebAPI方法使用对象实例化MyWidget属性,而MVC操作将其保留为null.
对我来说,WebAPI以这种方式运行似乎并不直观.它为什么要这样做?在这方面,我可以使其表现得像MVC一样吗?
@H_301_10@$.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”将参数作为form-url编码数据发送.
应用程序/ x-WWW窗体-urlencoded
ID 29
进myWidget
ID = 29&安培;进myWidget =
所以它被完全正确地反序列化了. MyWidget是空字符串,因此它将具有Widget类的空值.
另外我建议你为WebApi控制器添加Formatters配置:
@H_301_10@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调用.
UPDATE
传递给MVC控制器的form-url编码数据的主要区别将由运行时处理,最后由DefaultModelBinder处理(如果自定义绑定器不可用).因此,编码为form-url的数据通常用于MVC,因为通常数据是由HTML表单发布的.但Web API并不依赖于任何特定的编码设计.因此它使用特定的机制(格式化程序)来解析数据…例如json就像它在上面一样. System.Net.Http.Formatting中的FormUrlEncodedMediaTypeFormatter和System.Web.Mvc中的DefaultModelBinder处理空字符串的方式不同.
对于DefaultModelBinder,空字符串将转换为null.
分析代码我可以决定BindModel方法首先创建空模型:
@H_301_10@ if (model == null) { model = CreateModel(controllerContext,bindingContext,modelType); }
之后它将填充属性:
@H_301_10@// 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,其中字符串被处理如下:
@H_301_10@ string valueAsString = value as string; if (valueAsString != null && String.IsNullOrWhiteSpace(valueAsString)) { return null; }
我想没有任何标准描述从url编码的字符串到C#对象的转换.所以我不知道哪一个是正确的.无论如何,我确信你需要通过AJAX调用传递json而不是form-url编码数据.