Как я могу опубликовать (как 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 так:
- поля расположены в другом порядке
- RestSharp не включает в себя дополнительные пространство имен XMLSchema-instance namespace
- DataContractSerializer не содержит пробелов или разрывов строк (я добавил их выше для удобства чтения)
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 ответа:
Проблема Выше заключается в том, что 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
неправильно подбирает правильный сериализатор; вы можете попробовать переопределить эту функцию.