我一直在用我的头撞墙两天,希望有人可以帮我一把。我所拥有的是使用WCF编写的RESTful Web服务;实际上只有两个接受单个字符串参数并返回字符串的方法。参数和返回值都是直接的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>
这很有效但如果我将网址更改为:http://myuri.com/service.svc/method1/<foo&gt;我得到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>
正如您所看到的,我正在使用切换值'All'来捕获执行此服务期间发生的每个事件。我使用URL格式跑回了几次,这种格式可以验证跟踪器是否正常工作。然后我转到包含XML标记foo的URL并按预期收到400错误,但是当我回到日志文件时,没有附加信息附加到它的末尾。这使我相信在调用WCF服务之前显示400错误。
最后,我将方法从'GET'方法切换到'POST'方法,使用WebRequest / WebResponse写了一些代码,结果相同。现在我已经阅读了一些帖子,谈论在客户端使用XmlSerializer将数据发送到服务但是这违背了这项服务的目的。当我使用.NET编写服务时,很可能PHP或经典ASP脚本将连接到此服务,显然,他们无法访问XmlSerializer。
所以我的百万美元问题是这样的:是否可以向WCF中开发的RESTful Web服务发送“原始”XML请求,如果是,如何?
P.S。 进入和退出服务的XML不是基于任何有形对象,它只是我创建用于此服务的结构。进入的XML通过XPath进行解析,将值放入更大的XML字符串中,并传递给外部API。该API的结果将被处理,然后由我的RESTful服务返回。
非常感谢任何帮助!!
答案 0 :(得分:3)
布雷特,谢谢你的代码。不幸的是,我在这个帖子中找到了它:WCF Rest parameters involving complex types并在此帖之前尝试过它。
无论如何,我已经解决了这个问题。现在我想说我有一个完整的'Eureka'时刻,所有事情都在一起,但事实是我刚刚开始向Google投放缩写词,其中一个SERP引导我进入这个链接:http://blogs.msdn.com/pedram/archive/2008/04/21/how-to-consume-rest-services-with-wcf.aspx
链接本身并没有直接解决手头的问题,但它使我对如何整理我的URI模板有不同的看法。我已经阅读了这篇关于如何组建RESTful服务的MSDN文章http://msdn.microsoft.com/en-us/library/dd203052.aspx。在该示例中,作者提供了几个不同的模板,其中一些模板使用典型的查询字符串参数模式,而另一些则不使用。无论出于何种原因,我选择的模板都没有典型的查询字符串参数,这可以在我的原帖中看到。所以我修改了我的代码并想出了这个:
[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 OK。
感谢大家的帮助。
答案 1 :(得分:3)
原始代码不起作用的主要原因之一是因为如果在url路径或查询字符串中传递任何xml字符串数据,则需要对其进行url编码。
但是,在我看来,如果您希望客户端将数据作为xml发送到您的服务方法,则应该在网址中不。该URL具有不确定的最大长度,具体取决于浏览器,iis的版本以及位于服务器上的客户端之间的任何Web代理。
这意味着在请求正文中发送数据,这意味着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字符串,那么情况也是如此。
答案 2 :(得分:1)
我不相信您能够在URL中传递原始XML,但您可以在代码中执行此操作。我已经为在Compact Framework上运行的RESTful Web服务编写了客户端,可以将对象反序列化为原始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;
}
答案 3 :(得分:1)
据我所知,提供RESTful API的WCF服务不允许大型有效负载。实际上,默认情况下,WCF服务用于来回发送小消息。
在我测试的机器上,我无法从通过大于 2.7MB 的REST-ful Web端口公开的WCF服务返回XML有效负载(XML文件)。 / p>
从挖掘和搜索到大量的WCF文档,我得出的最终结论是,WCF是为缓冲模式下的小消息而设计的。当切换到流式模式时,可以来回传递大型消息。
但是,如果您以RESTful方式通过IIS公开WCF服务,则无法获得流式响应,因为不支持。或者至少我永远无法弄明白。
我希望我能为您提供一个很棒的代码示例答案,但是从我自己的实验中可以看出,无法从通过IIS端点公开的WCF服务返回大型XML有效负载强>
我的结论是,WCF实际上是一个网络解决方案,正在改进(很差)进入IIS以尝试提供Web服务。我鼓励使用ASP .NET MVC来创建RESTful webservices而不使用WCF。