我正在尝试在Silverlight中为我开发的RESTful WCF服务编写Web服务客户端代码。在Silverlight中,我使用DataContractSerializer实例构建WebRequest的主体。
如果OperationContract有一个参数,这种方法很有用。如果OperationContract中定义了多个参数,则它不能很好地工作。我相信这是因为WCF创建了一个以OperationContract命名的动态类型,并且该类型的成员以为该操作定义的参数命名。动态类型的目的是确保在提交给WCF服务的消息体中存在单个XML元素......这是有意义的。问题是我如何自己构建这个动态类型,以便我可以自己将它提交给DataContractSerializer。
第一个示例是定义单个参数的工作示例。第二个例子是我想要解决的场景(多个参数)。
示例1:
[OperationContract,
WebInvoke(Method = HttpMethodType.Post,
BodyStyle = WebMessageBodyStyle.Bare,
UriTemplate = "UnregisterProvider"),
WebHelp(Comment = "Unregistered the provider type with the specified URI.")]
void UnregisterProvider(RdfUri providerUri);
用于序列化邮件正文的代码:
StringBuilder msgBody = new StringBuilder(250);
using (XmlWriter xw = XmlWriter.Create(msgBody))
{
var serializer = new DataContractSerializer(typeof(RdfUri));
serializer.WriteObject(xw, providerUri);
}
结果身体:
<RdfUri xmlns="http://schemas.datacontract.org/2004/07/Intellidimension.Rdf">esp:semanticserver</RdfUri>
示例2:
[OperationContract,
WebInvoke(Method = HttpMethodType.Post,
BodyStyle = WebMessageBodyStyle.WrappedRequest, /* WrappedRequest must somehow signal WCF to create the anonymous type as it is required for multiple parameter OperationContracts */
UriTemplate = "RegisterProvider"),
WebHelp(Comment = "Registered a provider type with the specified URI.")]
void RegisterProvider(PoolableEntityServiceProviderDescriptor descriptor, RdfUri providerUri);
用于序列化邮件正文的代码:
//?????
结果身体:
<RegisterProvider xmlns="http://tempuri.org/">
<descriptor i:type="a:SemanticServerProviderDescriptor" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:a="http://schemas.datacontract.org/2004/07/Intellidimension.RdfEntity.Service.DataContracts">
<a:ConnectionString>Data Source=.\sqlexpress;Initial Catalog=RdfTest1;Persist Security Info=True;User ID=sa;Password=password</a:ConnectionString>
<a:ProviderGraphUri>http://entitystore/graph-provider</a:ProviderGraphUri>
</descriptor>
<providerUri>esp:semanticserver</providerUri>
</RegisterProvider>
更新1 :
以下是MSDN论坛上的一位提问类似问题的人:Can I use DataContractSerializerOperationFormatter to format a list of parameters from client to server?
DataContractSerializerOperationFormatter是一个内部类。所以看起来我可能必须为我的客户实现它的行为。
更新2 :
有些人在问我为什么不只是使用服务引用生成的普通Silverlight WCF客户端。原因是服务器上的WCF服务是RESTful服务。来自docs:
没有提供WCF中提供的WebHttpBinding的模拟信息。要从Silverlight 2访问纯HTTP,REST,RSS / Atom或AJAX服务,请使用直接访问基于HTTP的服务中描述的技术,例如WebClient类。要访问ASP.NET AJAX服务,请参阅访问ASP.NET AJAX服务。
答案 0 :(得分:1)
我实际上没有尝试过这个,但this article可能有你想要的。他通过这样做创建了一条包装好的消息:
private void ConsumeWcfRest()
{
string url = "http://something/MyWebsite/tagservice";
Tag tag1 = new Tag() { ID = 1, TagName = "test1" };
Tag tag2 = new Tag() { ID = 2, TagName = "test2" };
HttpWebRequest req = HttpWebRequest.Create(url + "/tags/wrapped/1") as HttpWebRequest;
req.Method = "POST";
string content = "<DoSomethingWrapped xmlns='http://mytagservice'>";
content = content + DoSerialize(tag1, "tag1");
content = content + DoSerialize(tag2, "tag2");
content = content + "</DoSomethingWrapped>";
// .....
}
private string DoSerialize(object obj, string rootName)
{
System.Runtime.Serialization.DataContractSerializer se;
if (string.IsNullOrEmpty(rootName))
se = new System.Runtime.Serialization.DataContractSerializer(obj.GetType());
else
se = new System.Runtime.Serialization.DataContractSerializer(obj.GetType(), rootName, "");
System.IO.MemoryStream ms = new System.IO.MemoryStream();
se.WriteObject(ms, obj);
ms.Position = 0;
byte[] arr = new byte[ms.Length];
ms.Read(arr, 0, Convert.ToInt32(ms.Length));
return new System.Text.UTF8Encoding().GetString(arr);
}
所以基本上他使用普通的DataContractSerializer
来序列化每个参数,然后手动将XML标签放在那里。这看起来有点像hacky,但它可能有用......
您可能想要开始反编译WCF库,看看您是否可以首先了解WCF是如何做到的。
答案 1 :(得分:0)
让我问你一个问题。让我们说你可以让这个工作。 POST请求正文的内容类型是什么?
您试图假装POST可以发送两件事,从而滥用HTTP。如果你想做你正在做的事情,那么为什么不使用常规的WCF服务合同。用这些参数发送多个参数很容易。
System.ServiceModel.Web的问题在于它只有90%将客户端与WCF分离。因此,要使事情真正正常工作,您需要在客户端进行WCF。此时为何不坚持常规的WCF服务合同。
如果你真的想按照预期使用HTTP,那么使用一种有助于而不是阻碍的技术。