如何在我的REST WCF服务中接受任意JSON对象? [英] How can I accept arbitrary JSON objects in my REST WCF service?

查看:83
本文介绍了如何在我的REST WCF服务中接受任意JSON对象?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想实现这样的服务方法:

I want to implement a service method like this:

[OperationContract]
[WebInvoke(RequestFormat = WebMessageFormat.Json, ResponseFormat=WebMessageFormat.Json)]
public void MakeShape(string shape, string color, IDictionary<string, object> moreArgs)
{
    if (shape == "circle")
    {
        MakeCircle(color, moreArgs);
    }
}

我的客户发布POST对象,例如:

My clients POST objects like:

{
    "shape":"circle",
    "color": "blue",    
    "radius": 42,
    "filled":true,
    "annotation": {"date":"1/1/2012", "owner":"George"}
}

在调用MakeCircle时,moreArgs将具有3个条目("radius","filled"和一个名为"annotation"的词典,其中包含2个键值对).

At the call to MakeCircle, moreArgs would have 3 entries ("radius", "filled", and a dictionary named "annotation" which contains 2 key-value pairs.)

到目前为止,我最好的是:

The best I've got so far is:

//Step 1: get the raw JSON by accepting a Stream with BodyStyle=WebMessageBodyStyle.Bare
[OperationContract]
[WebInvoke(RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle=WebMessageBodyStyle.Bare)]
public void MakeShape(Stream jsonStream)
{
    //Step 2: parse it into a Dictionary with JavaScriptSerializer or JSON.net
    StreamReader reader = new StreamReader(jsonStream);
    JavaScriptSerializer jsSerializer = new JavaScriptSerializer();
    Dictionary<string, object> args = jsSerializer.Deserialize<Dictionary<string,object>>(reader.ReadToEnd());            

    //Step 3: manually lookup and cast the "standard" arguments, and remove them from the Dictionary
    string shape = (string)args["shape"];
    string color = (string)args["color"];            

    //Step 4: make the original call, passing the remaining Dictionary as moreArgs
    MakeShape(shape,color,args);
}

我可以接受这样的解决方案,只是第3步很难在多种方法之间保持同步.显然,某事必须打开字典并使用额外的参数,但是我宁愿将该代码保留在我的通信层之外.在IMO中,它进入了知道参数的业务逻辑(在这种情况下,由MakeCircle表示).

I could live with a solution like this except step 3 will be a pain to keep in sync across dozens of methods. Obviously something has to open the dictionary and use the extra arguments but I'd rather keep that code out of my communications layer. IMO it goes inside the business logic that knows about the arguments (in this case represented by MakeCircle).

我真的很喜欢WCF的自动绑定,因为它消除了这些容易出错的手动翻译.我希望有一种方法可以对几乎所有内容使用它,除了为它不知道如何映射的参数指定一些额外的逻辑.也许有某种服务行为说将它们传递给[此代码],我会处理"?

I really like WCF's automatic binding because it eliminates these error-prone manual translations. I wish there were a way to use it for almost everything, except specify a little extra logic for the arguments it doesn't know how to map. Perhaps there's some sort of service behavior that says "pass them to [this code] and I'll deal with it"?

我已经考虑过IExtensibleDataObject提供的往返"支持,但是它似乎并不能使我的代码访问未知属性-它们被包装起来只是为了发送回客户端. http://msdn.microsoft.com/en-us/library/ms731083.aspx

I've considered the "round-tripping" support offered by IExtensibleDataObject, but it doesn't seem to give my code access to the unknown properties - they're wrapped up for the sole purpose of sending back to the client. http://msdn.microsoft.com/en-us/library/ms731083.aspx

另一种选择是使用包含IDictionary的自定义类,然后以某种方式自己接管反序列化.因此,服务方法为: [运营合同] [WebInvoke(RequestFormat = WebMessageFormat.Json,ResponseFormat = WebMessageFormat.Json,BodyStyle = WebMessageBodyStyle.WrappedRequest)] 公共无效MakeShape(字符串形状,字符串颜色,MoreArgs moreArgs)

Another option would be to use a custom class that contains a IDictionary, and somehow take over the deserialization myself. So the service method would be: [OperationContract] [WebInvoke(RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest)] public void MakeShape(string shape, string color, MoreArgs moreArgs)

我不得不强迫客户建立更严格的结构:

And I'd have to force clients into a stricter structure:

{
    "shape":"circle",
    "color": "blue",
    "moreArgs":{
        "radius": 42,
        "filled":true
        "annotation": {"date":"1/1/2012", "owner":"George"}
        }
}

那不是理想的,但是我可以忍受.问题变成了如何定义MoreArgs并正确填充一个.我的下一个尝试:

That's not ideal, but I could live with it. The question becomes how to define MoreArgs and get one populated properly. My next try:

[DataContract]
public class MoreArgs : ISerializable
{
    public Dictionary<string, object> Properties;
    public MoreArgs(SerializationInfo info, StreamingContext context)  
    {
        Properties = new Dictionary<string, object>();  
        foreach (var entry in info)  
        {                      
        Properties.Add(entry.Name, entry.Value);  
        }
    }  
    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        foreach (string key in Properties.Keys)
        {
        info.AddValue(key, Properties[key]);
        }  
    }
}

这会在服务启动时引发InvalidDataContractException(... MoreArgs不能被ISerializable并具有DataContractAttribute属性.)

This throws an InvalidDataContractException on service start (... MoreArgs' cannot be ISerializable and have DataContractAttribute attribute.)

删除[DataContract]属性会引发我期望的InvalidDataContractException(... MoreArgs无法序列化.请考虑使用DataContractAttribute属性对其进行标记...).

Removing the [DataContract] attribute throws the InvalidDataContractException I expect (...MoreArgs' cannot be serialized. Consider marking it with the DataContractAttribute attribute...).

也如预期的那样,删除ISerializable继承会清除该异常,但会导致在调用MakeCircle时moreArgs.Properties为空.

Also as expected, removing the ISerializable inheritance clears the exception, but results in moreArgs.Properties being null at the call to MakeCircle.

我还想知道是否可以使用某些混合解决方案?也许:

I also wonder if there's some hybrid solution I can use? Perhaps:

  • 像我第一次尝试的那样接受流并构建参数字典
  • 使用MoreArgs参数定义方法,例如我稍后的尝试
  • 从流中提取的字典中填充MoreArgs对象
  • 以某种方式重新调用WCF,说调用具有这些自变量的方法"(指定原始自变量字典,以及新的适当填充的MoreArgs).

MoreArgs也将包含原始参数,但这可能不是灾难.我想我可能可以使用反射进行呼叫,但是当WCF必须在内部具有此功能,进行调试和优化以进行启动时,这感觉很愚蠢.

MoreArgs would then contain the original arguments too, but that's probably not a disaster. I think I could probably make the call I need using reflection, but that feels silly when WCF must have this function internally, debugged and optimized to boot.

推荐答案

@Melissa Avery-Weir

@Melissa Avery-Weir

我对自己的解决方案"不满意,但我必须向前迈进.

I'm not happy with my "solution" but I had to move forward.

在应用启动时,对于我要调用的每个方法,我都会在查询表中填充一个MethodInfo.它由接口和方法名称键控.我使用反射和自定义属性来找到它们,但是许多技术都可以在这里使用.

At app startup, for every method I want to call, I stuff a MethodInfo in a lookup table. It's keyed by an interface and method name. I use reflection and a custom attribute to find them, but any number of techniques could work here.

我的一个WCF服务方法接受一个接口名称,一个方法名称和一个Stream作为参数.我使用JSON.NET将字典的参数反序列化,然后将其传递给调度程序.

My one WCF service method accepts an interface name, a method name, and a Stream as arguments. I use JSON.NET to deserialize arguments to a Dictionary and pass that to my dispatcher.

调度程序通过接口和方法名称查找MethodInfo.然后,将字典中的参数与MethodInfo中的参数进行匹配,我将填充一个参数数组.如果目标方法实际上具有Dictionary moreArgs参数,则它将获取所有不匹配的参数.最后,我调用MethodInfo.Invoke,并传递新填充的参数数组.

The dispatcher looks up the MethodInfo by interface and method name. Then, matching arguments from the dictionary to parameters in the MethodInfo, I fill an argument array. If the target method actually has a Dictionary moreArgs parameter, it gets any unmatched arguments. Finally I call MethodInfo.Invoke, passing the freshly populated argument array.

要做很多WCF几乎为我做的事情的代码很复杂,但是我没有找到更好的解决方案.

It's a lot of fiddly code to do some things that WCF almost does for me, but I didn't find a better solution.

自己控制所有这些都具有一些好处.我最喜欢的功能是能够使用保存的MethodInfos以我想要的任何语言自动生成客户端调用存根.

There are some benefits of controlling all this myself. My favorite is the ability to use the saved MethodInfos to automatically generate client-side call stubs in whatever language I want.

如果事实证明延迟绑定是一个性能问题,我将考虑将所有呼叫手动放在一个大的switch(methodName)中.如果我的界面仍然经常更改,则可以尝试生成该样板绑定代码作为构建步骤.也许我永远都不会打扰,因为我将成为数据库访问的瓶颈.

If the late binding turns out to be a performance issue I'll consider putting all the calls manually in a big switch(methodName). If my interfaces are still changing frequently, I may try to generate that boilerplate binding code as a build step. Probably I'll never bother since I'll be bottlenecked on DB access.

这篇关于如何在我的REST WCF服务中接受任意JSON对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆