雖然基於Web Service 的SOAP和POST並不總是加載數據的理想機制,但它們仍然適合用於對方法的調用,特別是對複雜數據的輸入或修改。使用WCF時,並不真正需要作出一區分——同樣的WCF方法能夠像簡單XML終結點或SOAP終結點一樣發佈。服務層將完全由AJAX界面分開,既不需要改變服務中的代碼,也不需要對AJAX或者任何其他客戶端技術指定任何的約束。事實上,可以選擇在一些替代的傳輸機制上發佈WCF應用。例如MSMQ、TCP/IP,甚至SMTP。
注意:
Wiki是爲了便於在Web上進行溝通而開發的一種記事本類應用程序,其設計宗旨是能夠高效、簡便地使用。下例中同時建立一個知識基礎應用來學習使用Wiki技術。更多 關於Wiki的信息參見 http://en.wikipedia.org/wiki/Wiki
建立簡單的Web Service來記錄應用狀態的消息。首先,定義一個名叫WikilData的數據類,這個類是WCF服務的起始點——它定義了服務返回的消息。可以使用DataContract和DataMember屬性來定義數據結構,在WikiData類中,使用DataContract屬性來定義元素的命名空間,使用DataMember屬性來定義XML序列元素(WikiData類的定義參見下面代碼)。定義這個協議後,就能夠編寫任何針對這種數據格式的客戶端應用。
[DataContract(Name = "WikiData",
Namespace = "ServiceOrientedAjax.Examples")]
public class WikiData
{
[DataMember]
public string Title { get; set; }
[DataMember]
public string Body { get; set; }
}
輸出的格式如下:
<WikiData xmlns="ServiceOrientedAjax.Examples" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Body>Hello WCF World!</Body>
<Title i:nil="true" />
</WikiData>
在定義了數據協議之後,下一步是實現服務。可以是用VS模板或者通過定義標有ServiceContract屬性的類來創建服務。ServiceContract屬性將這個類標記爲WCF的服務實現。每一個作爲Web Service終結點發布的方法都必須使用OperationContract屬性進行標識。OperationContract屬性與ASP.NET Web Service中使用的WebMethod相似,定義了一個通過WCF終結點發布的方法。
由於定義具有DataContract屬性的數據類WikiData,因此能夠簡單使用WikiData作爲Get請求的返回類型以及Set操作的輸入參數。下列程序定義了一個簡單WCF服務,此服務使用上例中的wikiData數據協定。
[ServiceContract(Namespace = "ServiceOrientedAjax.Examples")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class Service1
{
[OperationContract]
public void SetWiki(WikiData wiki)
{
HttpContext.Current.Application[wiki.Title] = wiki.Body;
}
[OperationContract]
[WebGet]
public WikiData GetWiki(string title)
{
WikiData wiki = new WikiData
{
Title=title,
Body=(string)HttpContext.Current.Application[title] ?? "Hello WCF World!"
};
return wiki;
}
}
在定義了WCF服務之後,需要在ASP.NET應用中通過服務文件(SVC文件)和web.config定義終結點。SVC文件時ASP.NET中用來激活WCF服務終結點的簡單文本文件,該文件中應當包含一下對 Service1 WCF終結點的引用:
<%@ ServiceHost Service="WcfServiceLibrary1.Service1" %>
除了在SVC文件中定義服務終結點之外,還必須在web.config中定義終結點和行爲。這些行爲可以影響多個終結點,並且能夠改變終端訪問WCF終結點的方式。在這個例子中,定義了POX終結點以及包括enableWebScript在內的JSON-enable終結點。這些增加的行爲可以通過behaviorConfiguration元素來啓用。
爲了啓用ASP.NET AJAX來爲服務建立JavaScript代理類,可以指定能夠映射到多個WCF終結點的enableWebScript行爲。下面的例子定義了名爲JsonBehavior的行爲配置項(JsonBehavior 是隨意選擇的命名),其中包含着enableWebScript行爲:
<behavior name="JsonBehavior">
<enableWebScript />
</behavior>
enableWebScript行爲使用JavaScript對象符號來定義數據,將默認響應從XML格式轉換JSON格式。在手工處理數據並且不能使用XSLT轉換時,這種格式比較易於處理。由於WCF從實現中抽象出終結點行爲,因此可以在web.config內定義XML終結點和JSON終結點而不會導致服務的任何改變。非常重要的一點是:這種映射通過配置而不是服務來開發完成。
爲了定義一個負載的提供JSON數據的終結點,可以使用web.config將終結點映射到行爲配置上。例如將一個附加的終結點(這是一個具備與Service.svc終結點同樣實現的JSON-enable終結點)映射到Service.svc/json上。要定義這個終結點,需要使用一個子終結點元素來創建服務元素,終結點元素爲服務定義了地址、行爲配置、綁定以及協定。終結點的地址與激活WCF服務的.svc文件相關,behaviorConfiguration定義了終結點的附加行爲。接下來的例子爲Service1定義了服務中金額的,同時含有一個映射到/json、使用JsonBehavior配置的附加終結點。
<services>
<service name="WcfServiceLibrary1.Service1" behaviorConfiguration="Global">
<endpoint address="" behaviorConfiguration="PoxBehavior"
binding="webHttpBinding" contract="WcfServiceLibrary1.Service1" />
<endpoint address="json" behaviorConfiguration="JsonBehavior"
binding="webHttpBinding" contract="WcfServiceLibrary1.Service1" />
</service>
</services>
當通過具有enableClientScipt行爲的終結點訪問服務時,上面的數據協定會輸出如下的JSON數據類:
{"wiki":{"Title":"default","Body":"Hello WCF World!"}}
這個JSON格式數據流包含着與數據協定定義的XML輸出相同的內容。由於在腳本中,JSON流比較容易使用,因此如果你不使用XSLT轉換器,Microsoft將選擇它作爲腳本激活的Web Service的默認行爲。
由於已經具備了在web.config中爲/json終結點定義的enableWebScript行爲,ASP.NET AJAX運行環境會爲服務生產一個JavaScript代理,這個代理能夠調用WCF服務並獲取相應。爲了手工檢查JavaScript代理,要使用WCF終結點並在URL後面加上/jsdebug開工選項,例如: http://localhost/ajaxfundamentals/simpleservice.svc/json/jsdebug 。代理中會含有通過ASP.NET AJAX JavaScript框架調用Web Service的正確的JavaScript語法細節,包括用來調用和索引數據的序列化對象,此時,你所需要了解的就是直接調用的實例方法。對於每一個Web Service操作,在方法的參數中應當添加 onSuccess 、onFailed 以及 userContext。由於所有的網絡調用都是以異步方式的,因此必須爲響應傳遞迴調句柄。儘管能夠將任意對象作爲用戶上下文進行傳遞,但通常userContext對象是原樣傳遞給公共代理方法,包括GetWiki方法和SetWiki方法。實際的代理中包含更多的代碼,但通常你需要調用的方法是對於公共服務方法的JavaScript函數。
SetWiki:function(wiki,succeededCallback, failedCallback, userContext) {
/// <param name="wiki" type="ServiceOrientedAjax.Examples.WikiData">WcfServiceLibrary1.WikiData</param>
/// <param name="succeededCallback" type="Function" optional="true" mayBeNull="true"></param>
/// <param name="failedCallback" type="Function" optional="true" mayBeNull="true"></param>
/// <param name="userContext" optional="true" mayBeNull="true"></param>
return this._invoke(this._get_path(), 'SetWiki',false,{wiki:wiki},succeededCallback,failedCallback,userContext); },
GetWiki:function(title,succeededCallback, failedCallback, userContext) {
/// <param name="title" type="String">System.String</param>
/// <param name="succeededCallback" type="Function" optional="true" mayBeNull="true"></param>
/// <param name="failedCallback" type="Function" optional="true" mayBeNull="true"></param>
/// <param name="userContext" optional="true" mayBeNull="true"></param>
return this._invoke(this._get_path(), 'GetWiki',true,{title:title},succeededCallback,failedCallback,userContext); }
在引用ASP.NET AJAX生成的JavaScript代理類後,才能在AJAX應用中使用這段代碼,這就需要在Service節點中使用ScriptManager控件,定義對Service.svc的引用。爲了增加ServiceReference,需要把帶有Path參數的ServiceReference加到WCF終結點中,如下所示:
<asp:ScriptManager ID="ScriptManager1" runat="server">
<Services>
<asp:ServiceReference Path="~/Service.svc/json" /></Services>
</asp:ScriptManager>
下面是整體的程序清單:
HTML界面:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server">
<Services>
<asp:ServiceReference Path="~/Service.svc/json" /></Services>
</asp:ScriptManager>
<div id="MainContent" onclick="editMessage">
Loading....
</div>
<br />
<span style="border:solid 1px silver;" id="editButton" onclick="editMessage();">
Edit
</span>
<div id="EditorRegion" style="display:none;">
<textarea id="TheEditor" rows="10" cols="10">
</textarea>
<br />
<span style="border:solid 1px silver;" onclick="save();">
Save
</span>
</div>
</form>
<script language="javascript" type="text/javascript">
var wikiName = 'default';
function OnAjaxLoad() {
var userContext = new Object();
ServiceOrientedAjax.Examples.Service1.GetWiki(wikiName, getMessageSuccess, onMessageFailure, userContext);
}
function editMessage() {
$get('EditorRegion').style.display = '';
$get('editButton').style.display = 'none';
$get('MainContent').style.display = 'none';
$get('TheEditor').value = $get('MainContent').innerHTML;
$get('TheEditor').enabled = true;
}
function getMessageSuccess(wikiData, userContext) {
var EditorRegion = $get('EditorRegion');
EditorRegion.style.display = 'none';
var content = $get('MainContent');
content.innerHTML = wikiData.Body;
content.style.display = '';
$get('editButton').style.display = '';
}
function save() {
var msg = $get('TheEditor').value;
var userContext = new Object();
userContext.msg = msg;
userContext.title = wikiName;
var wiki = new Object();
wiki.Title = wikiName;
wiki.Body = msg;
ServiceOrientedAjax.Examples.Service1.SetWiki(wiki, onSaveSuccess, onMessageFailure, wiki);
$get('TheEditor').enabled = false;
}
function onSaveSuccess(response, wiki) {
getMessageSuccess(wiki, null);
}
function onMessageFailure(ex, userContext) {
var EditorRegion = $get('EditorRegion');
EditorRegion.style.display = 'none';
var content = $get('MainContent');
content.innerHTML = ex.get_message();
content.style.display = '';
}
Sys.Application.add_load(OnAjaxLoad);
</script>
</body>
</html>
WCF服務類:
using System;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
using System.Web;
using System.Runtime.Serialization;
namespace WcfServiceLibrary1
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Service1" in both code and config file together.
[ServiceContract(Namespace = "ServiceOrientedAjax.Examples")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class Service1
{
[OperationContract]
public void SetWiki(WikiData wiki)
{
HttpContext.Current.Application[wiki.Title] = wiki.Body;
}
[OperationContract]
[WebGet]
public WikiData GetWiki(string title)
{
WikiData wiki = new WikiData
{
Title=title,
Body=(string)HttpContext.Current.Application[title] ?? "Hello WCF World!"
};
return wiki;
}
}
[DataContract(Name = "WikiData",
Namespace = "ServiceOrientedAjax.Examples")]
public class WikiData
{
[DataMember]
public string Title { get; set; }
[DataMember]
public string Body { get; set; }
}
}
Web.config 配置:
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="PoxBehavior">
<webHttp />
</behavior>
<behavior name="JsonBehavior">
<enableWebScript />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="Global">
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<!-- -->
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
<services>
<service name="WcfServiceLibrary1.Service1" behaviorConfiguration="Global">
<endpoint address="" behaviorConfiguration="PoxBehavior"
binding="webHttpBinding" contract="WcfServiceLibrary1.Service1" />
<endpoint address="json" behaviorConfiguration="JsonBehavior"
binding="webHttpBinding" contract="WcfServiceLibrary1.Service1" />
</service>
</services>
</system.serviceModel>