如何在WCF DataService中接受JSON?

5
我正在尝试了解如何使用基于EF 4.1的WCF数据服务创建一个可以持久化JSON对象的RESTful Web服务。
我已经能够创建一个接受一组原始数据类型作为参数的GET请求的方法。但我不喜欢这种解决方案,我更希望发送一个带有JSON对象的POST请求来完成操作。
我发现框架无法将JSON序列化为对象,但我可以手动完成。我的问题是似乎无法读取POST请求的主体 - 主体应该是JSON负载。
以下是粗略的代码示例。我尝试了几个不同的迭代版本,但似乎无法从请求正文中获取原始JSON数据。
您有什么想法?是否有更好的方法?我只想POST一些JSON数据并进行处理。
    [WebInvoke(Method = "POST")]
    public void SaveMyObj()
    {
        StreamReader r = new StreamReader(HttpContext.Current.Request.InputStream);
        string jsonBody = r.ReadToEnd();  // jsonBody is empty!!

        JavaScriptSerializer jss = new JavaScriptSerializer();
        MyObj o = (MyObj)jss.Deserialize(jsonBody, typeof(MyObj));

        // Now do validation, business logic, and persist my object
    }

我的DataService是一个扩展了Entity Framework DataService的服务。
System.Data.Services.DataService<T>

如果我尝试将非原始值作为方法参数添加,我会在跟踪日志中看到以下异常:
System.InvalidOperationException, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
'Void SaveMyObj(MyNamespace.MyObj)' has a parameter 'MyNamespace.MyObj o' of type 'MyNamespace.MyObj' which is not supported for service operations. Only primitive types are supported as parameters.
1个回答

8

给你的方法添加参数。你还需要在WebInvoke上添加一些附加属性。

这里有一个例子(从记忆中获取,因此可能有一点错误)

[WebInvoke(BodyStyle = WebMessageBodyStyle.Wrapped, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, UriTemplate = "modifyMyPerson")]
public void Modify(Person person) {
   ...
}

使用类如下所示:

[DataContract]
public class Person {

[DataMember(Order = 0)]
public string FirstName { get; set; }

}

这是发送的json格式:
var person = {FirstName: "Anthony"};
var jsonString = JSON.stringify({person: person});
// Then send this string in post using whatever, I personally use jQuery

编辑:这是使用“包装”方法。如果不使用包装方法,则需要删除BodyStyle = ...,并将JSON字符串化,您只需执行JSON.stringify(person)。我通常使用包装方法,以防需要添加其他参数。

完整代码示例的编辑

Global.asax

using System;
using System.ServiceModel.Activation;
using System.Web;
using System.Web.Routing;

namespace MyNamespace
{
    public class Global : HttpApplication
    {
        protected void Application_Start(object sender, EventArgs e)
        {
            RouteTable.Routes.Add(new ServiceRoute("myservice", new WebServiceHostFactory(), typeof(MyService)));
        }
    }
}

Service.cs

using System;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;

namespace MyNamespace
{
    [ServiceContract]
    [ServiceBehavior(MaxItemsInObjectGraph = int.MaxValue)]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class MyService
    {
        [OperationContract]
        [WebInvoke(UriTemplate = "addObject", ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest)]
        public void AddObject(MyObject myObject)
        {
            // ...
        }

        [OperationContract]
        [WebInvoke(UriTemplate = "updateObject", ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest)]
        public void UpdateObject(MyObject myObject)
        {
            // ...
        }

        [OperationContract]
        [WebInvoke(UriTemplate = "deleteObject", ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest)]
        public void DeleteObject(Guid myObjectId)
        {
            // ...
        }
    }
}

并将此添加到 Web.config 文件中。

  <system.serviceModel>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
  </system.serviceModel>

我的服务似乎不允许非基元类型作为参数。我正在使用Entity Framework DataService(扩展System.Data.Services.DataService<T>)。如果我的参数中有任何非基元类型,我会收到一个异常:code... has a parameter 'MyNamespace.MyObj bv' of type 'MyNamespace.MyObj' which is not supported for service operations. Only primitive types are supported as parameters.code - codemonkey
1
这是一个不幸的缺陷...如果我是你,除非你从中获得了其他好处,否则我会放弃DataService<T>。我将编辑我的帖子,提供一个完整的代码示例,展示我使用的在我看来非常出色的方法。 - anthony sottile
好的建议,我不确定这是否是DataService<T>的限制。据我所知,使用该DataService可以自动进行REST CRUD操作(仅限GET请求)。但在这种情况下,我不需要自动执行CRUD,因为我需要在持久化之前执行一些验证和业务逻辑。我会尝试您的方法,并在此更新。 - codemonkey
иҝҷеҜ№жҲ‘йқһеёёжңүж•ҲгҖӮжҲ‘зҡ„й—®йўҳжҳҺжҳҫжҳҜз”ұдәҺжү©еұ•DataService<T>пјҢд»ӨдәәеӨұжңӣзҡ„жҳҜе®ғдёҚж”ҜжҢҒжңҚеҠЎж–№жі•и°ғз”Ёзҡ„еҜ№иұЎеҸӮж•°гҖӮ - codemonkey
2
@codemonkey 这实际上是 OData 标准的限制,而不是 WCF Data Services 本身的限制。您可以通过黑客手段来获取 post 数据。在 Glenn Gailey 的相关博客文章“将数据上传到服务操作”中简要提到了这一点:http://blogs.msdn.com/b/writingdata_services/archive/2011/07/05/uploading-data-to-a-service-operation.aspx 话虽如此,这样的黑客行为并不被推荐,因此应该序列化复杂类型(博客文章中也提到了这一点)。 - Nate Cook

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接