Как я могу опубликовать (как XML) объект в моем ApiController с помощью RestSharp?


У меня есть ASP.NET сайт MVC4 реализует REST API, который я использую из клиентского приложения. Мои методы ApiController принимают и возвращают сложные объекты, как XML.

Недавно я открыл RestSharp и начал перемещать свой клиентский проект к нему. Тем не менее, у меня есть реальные проблемы с этим. Кажется, что почти работает - это так близко, что я почти чувствую вкус успеха - но я просто не могу заставить его работать на 100%.

Объекты, которые я передаю через проволоку, выглядят что-то вроде этого:

// The object I'm passing across the wire
public class Example
{
    bool IsActive { get; set; }
    string Name { get; set; }
}

Мой метод ApiController выглядит следующим образом:

// My ApiController method
public HttpResponseMessage PostExample(Example example)
{
    if (ModelState.IsValid)
    {
        db.Examples.Add(example);

        db.SaveChanges();

        HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created, example);

        return response;
    }
    else
    {
        return Request.CreateResponse(HttpStatusCode.BadRequest);
    }
}

Проблема возникает, когда я пытаюсь разместить объект на своем веб-сайте, например:

var example = new Example () { IsActive = true, Name = "foo" };

var request = new RestSharp.RestRequest("/api/example", RestSharp.Method.POST);

request.AddBody(example, XmlNamespace);

var client = new RestClient();

client.BaseUrl = "foo.com";

var response = client.Execute<Example>(request);

Приведенный выше код действительно попадает в метод PostExample в моем ApiController, и у него есть пример объекта в качестве параметра. однако значения свойств объекта примера не совпадают с теми, которые я передал методу Execute! В одном случае активный член был ложным вместо истинного, хотя я также видел случай, когда имя члена было null, где оно должно было иметь значение.

Я провел некоторое исследование с помощью Fiddler, и кажется, что правильные значения создаются в XML, который генерирует RestSharp. Однако XML не совсем в том же формате, что веб-сервер выдает при выполнении GET. Различия неуловимы, но, кажется, делают разницу между тем, что работает, и тем, что не работает. Фреймворк на конце веб-сервера, по-видимому, чувствителен к ним различия в форматировании и в результате неверная интерпретация XML.

Вот XML, который я получаю от RestSharp:

<Example xmlns="http://schemas.datacontract.org/2004/07/ExampleNamespace">
  <Name>foo</Name>
  <IsActive>true</IsActive>
</Example>

Вот что я получаю при выполнении GET на веб-сервере (или при сериализации с помощью DataContractSerializer, что я и делал ранее):

<Example xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/ExampleNamespace">
  <IsActive>true</IsActive>
  <Name>foo</Name>
</TagDto>

В RestSharp версия имеет следующие отличия от версии сериализатор datacontractserializer так:

  1. поля расположены в другом порядке
  2. RestSharp не включает в себя дополнительные пространство имен XMLSchema-instance namespace
  3. DataContractSerializer не содержит пробелов или разрывов строк (я добавил их выше для удобства чтения)
Я удивлен, что любой из них имеет большое значение, но очевидно, что это так. Заметьте также, что до тех пор, пока я не добавил явное пространство имен в вызове AddBody, это отсутствовало в сгенерированном XML (очевидно), и объект примера, переданный в мой ApiController, был null.

Так или иначе, я заметил, что RestSharp позволяет вам переопределить сериализатор, и дает возможность использовать .Чистый XML-сериализатора. Я попытался использовать это (безрезультатно).

Вот что я добавил перед вызовом AddBody:

request.XmlSerializer = new RestSharp.Serializers.DotNetXmlSerializer(XmlNamespace);

..и вот что я получаю:

<?xml version="1.0" encoding="utf-8"?>
<Example>
  <Name>foo</Name>
  <IsActive>true</IsActive>
</Example>
Это явно не годится, не в последнюю очередь потому, что он начинается с XML-объявления, которое, как я полагаю, вызовет проблемы. Нет никакого способа, чтобы выключить его, потому что RestSharp производном классе предоставляет никакого способа, чтобы сделать это. Кроме того, нет пространства имен-и я не могу заставить его появиться в выходных данных независимо от того, как я пытаюсь установить пространство имен в RestSharp (в конструкторе для DotNetXmlSerializer, установив член пространства имен или передав пространство имен AddBody). На мой взгляд, этот класс-не более чем ловушка.

Похоже, что мой единственный вариант-создать свой собственный класс сериализатора и использовать DataContractSerializer внутри. Это правда, или я что-то упустил?

(кстати, я могу установить RequestFormat запроса в JSON и это просто работает - но я все равно хотел бы знать, как заставить это работать с XML).

2 3

2 ответа:

Проблема Выше заключается в том, что WebAPI использует DataContractSerializer (в отличие от XmlSerializer, который является тем, что вам нужно). Чтобы переключить его, измените глобальный.asax следующим образом.

 var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
 xml.UseXmlSerializer = true;

Однако, я предлагаю вам использовать RESTSharp форматеры для веб-API (вместо использования .Чистая форматирования). Это особенно полезно, если у вас есть циклические ссылки DTO (сериализаторы .net fx не справляются с этим слишком изящно).

В Глобальном Масштабе.эйсакс, изменения форматирования, по ввод

GlobalConfiguration.Configuration.Formatters.XmlFormatter = //RestSharp XML serializer here

Краткий обзор сериализации в WebAPI здесь и стоит просмотреть

У меня были некоторые проблемы с вызовами AddBody, которые неправильно сериализуют значения JSON, поэтому может быть некоторое сходство с вашей проблемой. Вместо AddBody, вы можете попробовать:

request.AddParameter("text/xml", xmlAsString, ParameterType.RequestBody);

Если это работает, вы можете посмотреть, как изменить второй параметр, чтобы быть объектом xml, и посмотреть, делает ли сериализатор то, что вы хотите.

Другой вариант может заключаться в том, что XmlMediaTypeFormatter.ReadFromStreamAsync неправильно подбирает правильный сериализатор; вы можете попробовать переопределить эту функцию.