jQuery调用WCF服务传递JSON对象

Jiang,Zhiming
20081228

下面这个示例使用了WCF去创建一个服务端口从而能够被ASP.Net页面通过jQuery的AJAX方法访问,我们将在客户端使用Ajax技术来与WCF服务进行通信。这里我们仅使用jQuery去连接Web Service,而不去使用ASP.Net AJAX库,至于为什么不使用AJAX库中,那是因为我们在项目中已经使用了jQuery,而它已经能处理全部的AJAX请求和所有功能,并且,如果我们一旦使用ASP.NET AJAX库,我们也必须多包含一个超过80Kb的数据量(调试模式下会更大),但这也不是说ASP.NET AJAX库不实用,事实上,如果使用了相同的类库,我们能够少写很多的额外代码,但这个例子就是为了说明,在没有一个好的客户端代理的情况下,我们是如何调用Web服务的。

WCF 服务:

我们先创建一个WebSite,然后添加一个AJAX-enabled WCF Service,从而创建WCF服务。(请确保你使用了正确的.Net Framework 版本,我用的是3.5)

jquery本地联调接口_JSON

在我们添加完service后,它会自动进入该service的后置代码文件,Go ahead and browse around the file for a second.

首先我们要做的是找到"AspNetCompatibilityRequirements"并将其值置为 "Allowed":

[AspNetCompatibilityRequirements( RequirementsMode = 

 

这个属性的设置可将我们的服务跑在ASP.NET compatibility模式,如果我们未将该值设为"Allowed",那么就无法通过ASP.Net去访问到该服务,这个属性是当你添加 "AJAX-enabled WCF Service" 时自动生成的。更多祥细请参看MSDN

现在再来看自动生成的后置代码文件,我们能发现已经存在了一个被标注了"OperationContract"特性的"DoWork()" 的方法,这个方法是自动生成的,我们将使用这个方法来完成下面的示例。我们再来为该方法添加一个"WebGet"的特性,并设"RequestFormat" 的值为 "Json." 我们再来为该方法加上另外的一个特性RequestFormat,WebGet 和Get动词一样,作用于一个UriTemplate(本文不对此作进一步讨论),RequestFormat 特性可以让我们接收JSON格式的请求。

我们的 "DoWork()" 方法如下:

[OperationContract] 

[WebGet( RequestFormat=WebMessageFormat.Json )] 
public void DoWork() 

{ 

    // Add your operation implementation here 
    return; 

}

 

对象的模型:

我们希望通过"DoWork()" 来传递一个叫“Person”的对象,那先来创建一个Person对象写入到当前类的头部,其包含字段和属性(Name, Age 和 the types of Shoes they own),这个类同时也作为所传递JSON的结构。

[Serializable] 

[DataContract( Namespace = "http://www.dennydotnet.com/", Name = "Person" )] 
public class Person 

{ 
    private string _name = string.Empty; 
    private int _age = 0; 


    [DataMember( IsRequired = true, Name = "Name" )] 
    public string Name 

    { 

        get { return _name; } 

        set { _name = value; } 

    } 


    [DataMember( IsRequired = true, Name = "Age" )] 
    public int Age 

    { 

        get { return _age; } 

        set { _age = value; } 

    } 


    [DataMember( IsRequired = true, Name = "Shoes" )] 
    public List<String> Shoes; 

}

 

我们已经为 "Person”类的名称和命名空间标注上了契约,我们仍需要为属性授于数据成员特性,为每个属性设置"IsRequired"并具体指定其名称。你只需要具体指定名字,如果它和属性名不一样。举例来说吧,如果你有一个属性叫作“Level”,但你在数据成员特性中赋值为“Rank”,现在我们要回去修改我们的“DoWork()”方法,去接收Person对象作为参数。具体参下面代码块。

[OperationContract] 

[WebGet( RequestFormat=WebMessageFormat.Json )] 
public void DoWork(Person p) 

{ 
    // Add your operation implementation here 
    return; 

}

对Web.Config文件进行配置:
我们仅需对web.config文件做出很小的修改就能对服务进行访问了。首先加入一个 serviceBehavior 用来允许Http Get请求,再添加一些调试选项帮助。代码如下:

Below </endpointBehaviors>
<serviceBehaviors> 
    <behavior name="ServiceAspNetAjaxBehavior"> 
        <serviceMetadata httpGetEnabled="true" httpGetUrl="" /> 
        <serviceDebug httpHelpPageEnabled="true" includeExceptionDetailInFaults="true" /> 
    </behavior> 
</serviceBehaviors>

在<services>这里</services>之间,你的代码如下:

<service name="Service" behaviorConfiguration="ServiceAspNetAjaxBehavior"> 
    <endpoint address="" behaviorConfiguration="ServiceAspNetAjaxBehavior" 

binding="webHttpBinding" contract="Service" /> 
    <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" /> 
</service>

 

A security note about the following line:


<serviceDebug httpHelpPageEnabled="true" includeExceptionDetailInFaults="true" /> 


Allowing exception details can expose internal application information including personally identifiable or otherwise sensitive information. Setting the option to true is only recommended as a way to temporarily debug your service!!

你的Web.config文件修改后如下图所示:

jquery本地联调接口_jquery本地联调接口_02

回到前端页面:
现在我们的服务已经被创建和配置了,再来关注前端页面部分吧(确保页面已引用jQuery.js文件),首先来创建一个简单的Json对象用于向service传递,我们在Person类结构的基础上创建该JSON对象。 

var mydata = { "Name":"Denny", "Age":23, "Shoes":["Nike","Osiris","Etnies"] }; 


如果你对JSON还不是很熟悉,我们可以把它看作一个对象,这个小工具帮助你来查看 (JsonViewer):

jquery本地联调接口_AJAX_03

我们需要使用WCF和jQuery进行Ajax通信,下面的代码创建了一个AJAX呼叫,在头部设置了GET方式,和内容类型为application/json,将url的路径置为WCF 服务的 svc 文件,并在其后加上/和所要执行方法的名称,在这里我们要调用的是DoWork()方法,data是用来传递参数,为了让jQuery不去自动处理我们的数据,将processData设为false,我们还加入了对success和error的处理,以更多的了解AJAX执行完之后的处理过程。

 

function sendAJAX(data) { 

    $.ajax({ 

        type: "GET", 

        contentType: "application/json", 

        url: "Service.svc/DoWork", 

        data: data, 

        processData: false, 

        success: function(msg){ 

                alert( "Data Saved!" ); 

        }, 

        error:function(XMLHttpRequest, textStatus, errorThrown){ 

                alert( "Error Occured!" ); 

        } 

    }); 

}

 

然而很不幸运,这里有一个小的问题,我们必须发送真正的JSON字符串,作为参数传递。但还没有简单的方法让JSON对象转换为字符串,如果你试了data.toString(),得到的会是一个 "[object Object]"值。这不是我们想要的。
所以在这里我们要修改方法使得它能将JSON转换成字符串。

Note* The JSON de/serialization handles Date/Time in a specific way. The json2string function below does not take this into account. I'm sure there are some implementations out there which will work with ASP.NET AJAX but this one does not. For more information on this you can go here.

Update [4/11/08]: The javascript below has a few issues so it's been suggested that you should use the JSON.org version to "stringify" your object. You can download the script from here.

Update [4/25/08]: Rick Strahl has modified the JSON.org script so that it will properly create the dates to work with ASP.NET AJAX (read his post)

function json2string(strObject) { 
var c, i, l, s = '', v, p; 

switch (typeof strObject) { 
case 'object': 
if (strObject) { 
if (strObject.length && typeof strObject.length == 'number') { 
for (i = 0; i < strObject.length; ++i) { 

     v = json2string(strObject[i]); 
if (s) { 

      s += ','; 

     } 

     s += v; 

    } 
return '[' + s + ']'; 

   } else if (typeof strObject.toString != 'undefined') { 
for (i in strObject) { 

     v = strObject[i]; 
if (typeof v != 'undefined' && typeof v != 'function') { 

      v = json2string(v); 
if (s) { 

       s += ','; 

      } 

      s += json2string(i) + ':' + v; 

     } 

    } 
return '{' + s + '}'; 

   } 

  } 
return 'null'; 
case 'number': 
return isFinite(strObject) ? String(strObject) : 'null'; 
case 'string': 

  l = strObject.length; 

  s = '"'; 
for (i = 0; i < l; i += 1) { 

   c = strObject.charAt(i); 
if (c >= ' ') { 
if (c == '\\' || c == '"') { 

     s += '\\'; 

    } 

    s += c; 

   } else { 
switch (c) { 
case '\b': 

      s += '\\b'; 
break; 
case '\f': 

      s += '\\f'; 
break; 
case '\n': 

      s += '\\n'; 
break; 
case '\r': 

      s += '\\r'; 
break; 
case '\t': 

      s += '\\t'; 
break; 
default: 

      c = c.charCodeAt(); 

      s += '\\u00' + Math.floor(c / 16).toString(16) + 

       (c % 16).toString(16); 

    } 

   } 

  } 
return s + '"'; 
case 'boolean': 
return String(strObject); 
default: 
return 'null'; 

} 

}

 

现在我们有一个方法可以将JSON对象转成我们所需要的字符串,现在我们回过头去修改我们之前定义的 "mydata" 变量,我们应当按下面去应用所写的json2string方法。

var mydata = { "Name":"Denny", "Age":23, "Shoes":["Nike","Osiris","Etnies"] }; 
var jsonStr = "p=" + json2string(mydata);

注意在下面我准备了"p=" 字符在我们的JSON字符串中,“p”对应于"DoWork()" 方法中的参数,当我们将参数改名为"Dude" ( 例如DoWork(Person Dude) )时,我们也必须将此处替换为"Dude="

现在我们准备好了查询字符串,我们参看到我们如何调用服务:
http://www.dennydotnet.com/Service.svc/DoWork/?p={ "Name":"Denny", "Age":23, "Shoes":["Nike","Osiris","Etnies"] }

 

你将接收到一个已经为URL编码处理过的值,如下:
http://www.dennydotnet.com/Service.svc/DoWork/?p=%7b+%22Name%22%3a%22Denny%22%2c+%22Age%22%3a23%2c+%22Shoes%22%3a%5b%22Nike%22%2c%22Osiris%22%2c%22Etnies%22%5d+%7d%3b

紧接着我们去调用"SendAjax()" javascript 方法,现在能够调试我们的服务并验证数据是否被传递到服务。如下图

jquery本地联调接口_WCF_04

 

现在你仅需要在DoWork()方法里实现自己的逻辑。注意无论你如何怎么去做 在WCF的服务端,它已经为你做好了。
现在你仅需实现一些异常管理以确定你不会得到一些非法数据,或者添加一些验证,那就留给你自己吧...