4

好久不见,第一次发帖。几天来我一直在努力解决这个问题,并希望得到任何提示。我在下面分解这个问题。

我想要实现的目标:
我想设置一个 JSON WCF Web 服务,但我想使用 JSON.net 序列化程序而不是 WCF 附带的序列化程序。为什么?因为我发现使用 WCF 附带的序列化会使集合膨胀(我将在下面展示我的意思的示例)。该服务的最终消费者将是一个 JavaScript 客户端,所以我不想让客户端在浏览 JSON 时做额外的麻烦。

我想出了一个我想要的简单示例,并着手尝试使其在 C# 中工作。我必须承认很长一段时间没有使用过 .NET,这可能是我速度慢的原因。无论如何,我有耐心的库存等等。

所以,我想要一个接受用户名并返回包含用户信息的 JSON 对象的服务。我设想该服务被称为这样的:

http://someHostName/whoareyou?username=paul

并让服务响应:

{
    "errorCode": 0,
    "payLoad"  :  {
        "userFullName": "Paul The Octopus",
        "userLevel"        : "Administrator"
    }
}

我可以轻松地在 JavaScript 或 PHP 中使用上述响应的 JSON 对象,并且对自己感觉非常好。

经过一番谷歌搜索后,我发现一些帖子表明在 .NET Framework 4 中使用 WCF 很容易做到这一点。不久前,我在课程中对 WCF 做了一些摆弄,但早就忘记了其中的 99%,所以我找到并关注了这篇文章以适应我的目标: http: //www.codeproject.com/Articles/105273/Create-RESTful-WCF-Service-API-Step-By-Step-Guide#

尝试 1:
按照上面的帖子,我能够设置一个 WCF 服务(代码如下),它可以满足我的需求,但生成的 JSON 输出有点臃肿,如下所示。

RestServiceImpl.svc.cs 文件

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using Newtonsoft.Json;
using System.Collections.Specialized;
//Based on this psot: http://www.codeproject.com/Articles/105273/Create-RESTful-WCF-Service-API-Step-By-Step-Guide

namespace RestService
{
    public class RestServiceImpl : IRestServiceImpl
    {
        #region IRestService Members

        public WhoAreYouResponse whoareyou(string username)
        {
            var payLoad = new Dictionary<string, string>
            {
                {"userFullName", "Paul The Octopus"},
                {"userLevel", "Administrator"}
            };

            WhoAreYouResponse whoAreYouResponse = new WhoAreYouResponse
            {
                errorCode = 0,
                payLoad   = payLoad
            };

            return whoAreYouResponse;
        }

        #endregion
    }

    //Helper bits to be used in service implementation
    public class WhoAreYouResponse
    {
        public int errorCode { get; set; }
        public Dictionary<string,string> payLoad { get; set; }
    }
}

IRestServiceImpl.cs 文件

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace RestService
{
    [ServiceContract]
    public interface IRestServiceImpl
    {
        [OperationContract]
        [WebInvoke(Method = "GET", 
            ResponseFormat = WebMessageFormat.Json, 
            BodyStyle = WebMessageBodyStyle.Bare, 
            UriTemplate = "json/whoareyou?username={username}")]
        WhoAreYouResponse whoareyou(string username);
    }
}

Web.config 文件

<?xml version="1.0"?>
<configuration>

  <system.web>
    <compilation debug="true" targetFramework="4.0" />
  </system.web>
  <system.serviceModel>
    <services>
      <service name="RestService.RestServiceImpl" behaviorConfiguration="ServiceBehavior">
        <endpoint address="" binding="webHttpBinding" contract="RestService.IRestServiceImpl" behaviorConfiguration="web">
        </endpoint>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="ServiceBehavior">
          <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
          <serviceMetadata httpGetEnabled="true"/>
          <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="false"/>
        </behavior>
      </serviceBehaviors>
      <endpointBehaviors>
        <behavior name="web">
          <webHttp/>
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
 <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
  </system.webServer>

</configuration>

*通过这样的浏览器调用服务的结果http://192.168.0.84/TestRestService/RestServiceImpl.svc/json/whoareyou?username=paul*

{
    "errorCode":0,
    "payLoad"   : [
        {"Key":"userFullName", "Value":"Paul The Octopus"},
        {"Key":"userLevel","Value":"Administrator"}
    ]
}

JSON 响应中的汤中的头发是 Dictionary 对象在 JSON 响应中映射到“payLoad”的方式。它是一个对象数组,而我期待一个 JSON 对象没有这个“键”、“值”业务。不完全是我想要的。关闭,但在客户端处理起来非常挑剔。没有人喜欢臃肿和不必要的工作,所以对此表示赞同。

为解决方案做更多的拖钓我读了一些 SO 帖子,表明这里发生的事情是该服务正在使用内置的序列化程序,并且不会以任何其他方式序列化“字典”。需要一些额外的工作才能以我期望的格式获得它。所以,我想,使用另一个序列化程序怎么样?按照这个想法,我在这里发现了这个坏男孩:http: //james.newtonking.com/projects/json-net.aspx

这导致了我在下面的第二次尝试。

尝试 2:
下载 JSON.NET 序列化程序并将其导入我的小项目中,我将以下文件更改为如下所示(将whoareyou方法的返回类型更改为string):

RestServiceImpl.svc.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using Newtonsoft.Json;
using System.Collections.Specialized;
//Based on this psot: http://www.codeproject.com/Articles/105273/Create-RESTful-WCF-Service-API-Step-By-Step-Guide

namespace RestService
{
    public class RestServiceImpl : IRestServiceImpl
    {
        #region IRestService Members

        public string whoareyou(string username)
        {
            var payLoad = new Dictionary<string, string>
            {
                {"userFullName", "Paul The Octopus"},
                {"userLevel", "Administrator"}
            };

            WhoAreYouResponse whoAreYouResponse = new WhoAreYouResponse
            {
                errorCode = 0,
                payLoad   = payLoad
            };

            return JsonConvert.SerializeObject(whoAreYouResponse);
        }

        #endregion
    }

    //Helper bits to be used in service implementation
    public class WhoAreYouResponse
    {
        public int errorCode { get; set; }
        public Dictionary<string,string> payLoad { get; set; }
    }
}

IRestServiceImpl.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace RestService
{
    [ServiceContract]
    public interface IRestServiceImpl
    {
        [OperationContract]
        [WebInvoke(Method = "GET", 
            ResponseFormat = WebMessageFormat.Json, 
            BodyStyle = WebMessageBodyStyle.Bare, 
            UriTemplate = "json/whoareyou?username={username}")]
        string whoareyou(string username);
    }
}

当服务被调用时,我得到这个响应:

"{
\"errorCode\":0,
\"payLoad\":{
    \"userFullName\":\"Paul The Octopus\",
    \"userLevel\":\"Administrator\"}
}"

优胜者!这就是我想要和期待的……但是。把卡车倒过来。整个 JSON 对象用双引号括起来!?嗯。这是一个包含 JSON 对象的字符串。我把头发从汤里拿出来,现在一只苍蝇飞了进去!

经过片刻的挠头后,很明显(我认为)正在发生的事情是 JSON.NET 序列化程序像一个魅力一样工作,在字符串中吐出一个 JSON 对象,但随后将其放入 WCF 序列化程序并本质上是字符串化的. 所以我在返回中看到的是一个 JSON 字符串。很近!事实上,我的方法whoareyou说它返回一个字符串,这几乎是我的错。

所以,我的问题是,如何让这个问题孩子停止这种双序列化业务?我找不到我的whoareyou方法的返回类型类似于 JSON 对象。有没有办法告诉 WCF 使用 JSON.NET 序列化程序,或者一些这样的解决方案?

非常感谢指针。

4

1 回答 1

6

据我了解,您正在从头开始创建服务,并且对如何实现 REST 服务没有任何限制。那我建议你使用 ASP.NET WebApi

http://www.asp.net/web-api

不要使用传统的 Web 服务技术,因为在较新的版本中,已经为您完成了许多样板代码。

使用 web api,您可以轻松替换或添加任何序列化程序/格式化程序。如何做到这一点在以下文章中描述:

http://www.asp.net/web-api/overview/formats-and-model-binding/media-formatters

http://www.asp.net/web-api/overview/formats-and-model-binding/json-and-xml-serialization

我遇到了您描述的序列化问题,并用这种方法解决了这些问题。根据我对旧 Web 服务技术(WCF 3.5 Rest Starter Kit、Wcf 4 REST)的经验,它可以以更难的方式完成。

于 2012-07-20T07:24:02.783 回答