2016-01-05 3 views
5

При публикации моделей JSON в методах контроллера WebAPI я заметил, что если в модели 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; 
    } 
} 

класс модель, которая будет получать публикуемую

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 модели для каждого контроллера:

$.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 в этом отношении?

+0

Нет никаких шансов, что ваша 'Model' построит' Widget' в своем конструкторе по умолчанию и назначит его 'MyWidget', не так ли? – dbc

+0

Какую версию веб-API вы используете? –

+0

@dbc, класс не такой, как вы его видите. Конструктор не указан. И кроме того, если бы это была проблема с конструктором, это произошло бы для обоих контроллеров. –

ответ

2

Я думаю, что это похоже на проблемы, ранее испытанные в наших проектах.

Вы должны изменить почтовый индекс для 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 посылает параметры, как данные формы для URL-кодирования.

применение/х-WWW-форм-urlencoded
ID 29
MyWidget

ID = 29 = & MyWidget

Так было десериализации совсем правильно. MyWidget - пустая строка, поэтому у нее будет пустое значение класса Widget.

Кроме того, я рекомендую вам добавить конфигурацию Форматтеров для контроллеров WebAPI:

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.

ОБНОВЛЕНИЕ

Главное отличие, что форма-URL-кодированные данные, передаваемые в контроллер MVC будет обработан время выполнения и, наконец, обрабатываются DefaultModelBinder (если заказ связующее не доступен). Таким образом, данные, закодированные как form-url, обычно используются для MVC, потому что обычно данные генерируются почтой HTML-формы. Но Web API не полагается на какую-либо конкретную кодировку по дизайну. Поэтому он использует конкретный механизм (formatters) для анализа данных ... например, json, как и выше. Таким образом FormUrlEncodedMediaTypeFormatter из System.Net.Http.Formatting и DefaultModelBinder из System.Web.Mvc обрабатывает пустую строку по-разному.

Для пустой строки 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 вернется fallbackBinder для типа Widget (тип недвижимости). И сама fallbackBinder будет называть ConvertSimpleType, где строка обрабатываются следующим образом:

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

Я предполагаю, что нет каких-либо стандартов, описывающих преобразование URL-закодированных строк на объекты C#. Поэтому я не знаю, какой из них правильный. В любом случае я уверен, что вам нужно передать json через вызовы AJAX, а не данные, закодированные в форме-url.

+0

Это сработало (в частности, используя $ .ajax), спасибо за понимание! Мне интересно, что в этом отношении действия MVC Action и WebAPI ведут себя по-другому. –

+0

В добавлении объяснения относительно разница в первоначально ответе. – Maxim

+0

Отличное объяснение, спасибо! Хотел бы я снова проголосовать за ваш ответ. –

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); 
     } 
    } 
+3

Не использует ли API-интерфейс Newtonsoft.Json по умолчанию API-интерфейс? –

+0

@NikolaiSamteladze, вы правы. Я сейчас проверил. Но Json.Net не инициализирует нулевые объекты. – halit

+0

Объект получает экземпляр во время привязки. Если я остановлюсь в действии контроллера, я вижу новый уже созданный объект (или null в случае контроллера MVC). –

Смежные вопросы