Служба RESTful WCF возвращает код 400 при отправке «сырого» XML - PullRequest
7 голосов
/ 07 января 2010

Я уже два дня бьюсь головой об стену, так что, надеюсь, кто-нибудь может мне помочь. У меня есть веб-служба RESTful, которую я написал с использованием WCF; на самом деле ничего, только два метода, которые принимают один строковый параметр и также возвращают строку. И параметр, и возвращаемое значение являются прямыми XML.

[ServiceContract]
public interface IService
{
    [OperationContract]
    [WebGet(UriTemplate = "/method1/{data}", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Xml, RequestFormat = WebMessageFormat.Xml)]
    string Method1(string data);

    [OperationContract]
    [WebGet(UriTemplate = "/method2/{data}", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Xml, RequestFormat = WebMessageFormat.Xml)]
    string Method2(string data);

}

Ради аргумента скажем, что реализация обоих этих методов выглядит следующим образом:

public string Method1(string data)
{
    return string.Format("You entered: {0}", data);
}

Если я захожу на http: //myuri.com/service.svc/method1/foo, в браузер записывается следующее:

  <string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">You 
    entered: foo</string> 

Это прекрасно работает, но если я изменю URL-адрес на: http: //myuri.com/service.svc/method1/, я получу 400 (неверный запрос). Поэтому я включил некоторые трассировщики, чтобы увидеть, что происходит, используя следующий код:

<system.diagnostics>
<sources>
  <source name="System.ServiceModel"
          switchValue="All">
    <listeners>
      <add name="traceListener"
          type="System.Diagnostics.XmlWriterTraceListener"
          initializeData= "c:\Traces.svclog" />
    </listeners>
  </source>
</sources>

Как видите, я использую значение переключателя «Все» для захвата каждого события, которое происходит во время выполнения этой службы. Я пробежал несколько раз, используя формат URL, который работает, чтобы убедиться, что трассировщики работали и были. Затем я перешел по URL-адресу, содержащему тег XML foo, и получил ошибку 400, как и ожидалось, но когда я вернулся к файлу журнала, никакой дополнительной информации к его концу не было. Это заставляет меня поверить, что ошибка 400 отображается до того, как служба WCF будет вызвана.

Наконец, я переключил методы с методов 'GET' на методы 'POST', написал немного кода, используя WebRequest / WebResponse с тем же результатом. Сейчас я прочитал несколько постов, в которых говорится об использовании XmlSerializer на стороне клиента для отправки данных в службу, но это противоречит цели этой службы. Хотя я использую .NET для написания сервиса, вполне вероятно, что PHP или классические ASP-скрипты будут подключаться к этому сервису, и они, очевидно, не имеют доступа к XmlSerializer.

Итак, мой вопрос на миллион долларов таков: возможно ли отправить «сырой» XML-запрос на веб-сервис RESTful, разработанный в WCF, и, если да, то как?

P.S. XML, входящий и выходящий из сервиса, не основан на каком-либо материальном объекте, это просто структура, которую я создал для использования с этим сервисом. Входящий XML анализируется через XPath, значения помещаются в большую строку XML и передаются во внешний API. Результаты этого API обрабатываются, а затем возвращаются моей службой RESTful.

Любая помощь будет принята с благодарностью !!

Ответы [ 4 ]

3 голосов
/ 07 января 2010

Бретт, спасибо за кусочек кода.К сожалению, я нашел это в этой теме: Параметры WCF Rest, включающие сложные типы , и пытался сделать это до публикации.

В любом случае, я решил эту проблему.Теперь я хотел бы сказать, что у меня был полный момент «Эврика», и все просто сошлось, но факт в том, что я только начал бросать акронимы в Google, и один из поисковиков привел меня к этой ссылке: http://blogs.msdn.com/pedram/archive/2008/04/21/how-to-consume-rest-services-with-wcf.aspx

Сама ссылка не имеет прямого отношения к рассматриваемому вопросу, но заставляет меня по-другому думать о том, как я собирал свои шаблоны URI.Я прочитал эту статью MSDN http://msdn.microsoft.com/en-us/library/dd203052.aspx о том, как собрать сервис RESTful.В этом примере автор предоставляет несколько различных шаблонов. Некоторые из них используют типовой шаблон строки запроса, а некоторые - нет.По какой-то причине я выбираю шаблон, в котором нет типичного параметра строки запроса, как видно из моего исходного поста.Поэтому я немного изменил свой код и придумал следующее:

[OperationContract] 
[WebGet(UriTemplate = "/method1/?xml={data}", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Xml, RequestFormat = WebMessageFormat.Xml)] 
string Method2(string data); 

Обратите внимание, что шаблон URI - единственное, что изменилось;переходя от "/ method1 / {data}" к "/ method1 /? xml = {data}. Затем я перешел к http://myuri.com/service.svc/method1/?xml= и альту, все прекрасно работало!

Это также было проблемойс POST. По любой причине передача XML в теле содержимого, даже в виде пары ключ / значение, приводила к ошибке 400. Используя точно такой же шаблон URI, как показано выше, я открыл Fiddler, выполнил POST, и в результате получилось200 ОК.

Спасибо за помощь всем.

3 голосов
/ 10 января 2010

одна из основных причин, по которой исходный код не работал, заключалась в том, что любые данные строки XML должны были бы быть закодированы в URL, если они были переданы в пути URL или в строке запроса.

Однако, по моему мнению,

, если вы хотите, чтобы клиент отправлял вам данные в виде XML в ваш метод обслуживания, тогда в URL-адресе должно быть , а не . URL имеет неопределенную максимальную длину, в зависимости от браузера, версии iis, а также любых веб-прокси, которые находятся между клиентом на сервере.

это означает отправку данных в теле запроса, что означает глагол, отличный от GET. Итак, давайте использовать POST.

объявляйте параметр 'data', как и раньше, в сигнатуре метода, но извлеките параметр из UriTemplate и сделайте его WebInvoke (глагол по умолчанию POST, как вы знаете).

[WebInvoke(UriTemplate = "/method1", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Xml, RequestFormat = WebMessageFormat.Xml)]  
string Method2(string data);

Тогда ваш запрос тела POST должен быть отформатирован следующим образом:

<data><![CDATA[data xml goes in here]]></data>

Почему раздел CDATA? Подумайте о типе целевого параметра - строка. Вы хотите передать XML в этой строке. Следовательно, вам нужно убедиться, что сериализатор WCF не воспринимает данные как сложные данные, которые должны быть прочитаны напрямую. То же самое будет верно, если ваш формат запроса - JSON, и вы хотите отправить строку JSON в службу.

1 голос
/ 15 января 2010

Насколько я понимаю, службы WCF, предоставляющие API-интерфейсы RESTful, не допускают больших полезных нагрузок. Фактически, по умолчанию службы WCF предназначены для отправки небольших сообщений туда и обратно.

На машине, которую я тестировал, я не смог получить полезную нагрузку XML (файл XML), возвращенную из службы WCF, предоставляемой через конечную точку веб-поиска REST, которая была больше 2,7 МБ .

Из копания и поиска через множество документации WCF, я пришел к окончательному выводу, что WCF предназначен для небольших сообщений в режиме с буферизацией . При переключении в режим streaming большие сообщения могут передаваться туда и обратно.

Однако, если вы предоставляете сервисы WCF через IIS в режиме RESTful, вы не можете получить потоковый ответ, поскольку он не поддерживается. Или, по крайней мере, я бы никогда этого не понял.

Хотелось бы, чтобы у меня был отличный пример кода для вас, но из моих собственных экспериментов я понял, что нет способа вернуть большие полезные нагрузки XML из службы WCF, предоставляемой через конечную точку IIS .

Мой вывод заключается в том, что WCF - это действительно сетевое решение, которое (плохо) встраивается в IIS для обеспечения веб-сервисов. Я бы рекомендовал вместо этого использовать ASP .NET MVC для создания веб-сервисов RESTful без использования WCF.

1 голос
/ 07 января 2010

Я не верю, что вы сможете передавать необработанный XML в URL, но вы можете сделать это в коде. Я написал клиенты для веб-служб RESTful, которые работают на Compact Framework, и у них нет проблем с десериализацией объектов в необработанный XML и отправкой их в службу через HttpWebRequest и HttpWebResponse.

Если вы делаете GET, вы просто создадите URL в коде. Если вы выполняете POST, вы можете присоединить XML в виде байтового массива (код ниже .Net, но, безусловно, вы можете сделать что-то подобное в PHP).

private HttpWebRequest DoInvokeRequest<T>(string uri, string method, T requestBody)
{
    string destinationUrl = _baseUrl + uri;
var invokeRequest = WebRequest.Create(destinationUrl) as HttpWebRequest;
if (invokeRequest == null)
    return null;

invokeRequest.Method = method;
invokeRequest.ContentType = "text/xml";

byte[] requestBodyBytes = ToByteArray(requestBody);
invokeRequest.ContentLength = requestBodyBytes.Length;
AddRequestHeaders(invokeRequest);

using (Stream postStream = invokeRequest.GetRequestStream())
    postStream.Write(requestBodyBytes, 0, requestBodyBytes.Length);


invokeRequest.Timeout = 60000;
return invokeRequest;
}

private static byte[] ToByteArray<T>(T requestBody)
{
    byte[] bytes;
using (var s = new MemoryStream())
{
    var serializer = new XmlSerializer(typeof (T));
    serializer.Serialize(s, requestBody);
    bytes = s.ToArray();
}
return bytes;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...