到目前为止我们已经看了如何使用WebHttpBinding绑定和WebHttpBehavior终结点行为来寄宿服务。这允许我们使用POX来暴露服务。很多网站开发人员想放弃使用XML而使用JSON,一个更简单的格式。JSON非常适合需要一个高效地从服务截取反馈消息的浏览器应用程序,它已经集成了JavaScript的优势,编程语言必须通常使用客户端网络开发。JSON是JavaScript的对象符号元素子集,这意味着你可以很容易地在JavaScript中创建对象。由于这个原因,XML与AJAX应用程序一起使用是很好的。
AJAX表示异步JavaScript和XML。基于AJAX的网络应用程序比传统的应用程序有重要的优势。它们允许改进的用户体验以及更好的带宽可供使用。这通过改进浏览器到服务器通信实现,以便于浏览器不需要执行一个页面载入。这通过使用JavaScript和XMLHttpRequest类与一台服务器异步通信。由于与服务器通信不需要载入界面就能完成,开发人员可以创建接近桌面应用程序的富用户接口体验。这些网络应用程序的类型通常是指富互联网应用程序,或者RIAs.
ASP.NET AJAX 集成
有很多创建这些基于AJAX的网络应用程序平台。其中一个有名的平台是ASP.NET AJAX 平台。这个平台有一个好的创建开启AJAX网络应用程序的客户端和服务端模型。它包含很多能力比如一个富客户端类库,富AJAX网页控件,为与服务通信而自动生成的客户端代理。它也基于ASP.NET,使用微软技术来使用.NET创建网络应用程序。WCF在.NET Framework 3.0 时也与ASP.NET 集成。.NET Framework 3.5 使用WebScriptEnablingBehavior终结点行为引入对ASP.NET AJAX 应用的支持。这取代了WebHttpBehavior终结点行为。它默认使用JSON和ASP.NET 客户端代理生成添加支持。这些新的特性可以通过使用enableWebScript配置元素来取代webHttp终结点行为配置元素使用。
我们创建一个称为XBOX 360 游戏审查程序来看我们如何使用WebHttpBinding绑定以及WebScriptEnablingBehavior来创建基于ASP.NET AJAX 的示例应用程序。这个简单的应用程序允许用户对他们喜爱的XBOX 360 游戏提供审查。这个应用使用Visual Studio 2008 中ASP.NET AJAX 网站模板创建。
图片13.2 显示了这个站点的图片。
图片13.2 XBOX 360 AJAX 开启应用的游戏审查视图
这个站点有很多特性。首先是在一个ListBox控件中显示给用户的游戏列表。用户可以选择一个游戏并查看每个游戏的评论。然后一个用户可以为每个游戏添加评论。列表13.7 列出了提供这个功能的服务。
列表13.7 GameReviewService.cs
using System; using System.Collections.Generic; using System.Runtime.Serialization; using System.ServiceModel; using System.Text; using System.ServiceModel.Activation; using System.ServiceModel.Web; namespace EssentialWCF { [ServiceContract(Namespace = "EssentialWCF")] [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)] [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] public class GameReviewService { private string[] gamelist = new string[] { "Viva Pinata", "Star Wars Lego", "Spiderman Ultimate","Gears of War", "Halo 2", "Halo 3"}; private Dictionary<string, List<string>> reviews; public GameReviewService() { reviews = new Dictionary<string, List<string>>(); foreach (string game in gamelist) { reviews.Add(game, new List<string>()); } } [OperationContract] [WebGet] public string[] Games() { return gamelist; } [OperationContract] [WebGet] public string[] Reviews(string game) { WebOperationContext ctx = WebOperationContext.Current; ctx.OutgoingResponse.Headers.Add("Cache-Control", "no-cache"); if (!reviews.ContainsKey(game)) { return null; } List<string> listOfReviews = reviews[game]; if (listOfReviews.Count == 0) { return new string[] { string.Format("No reviews found for {0}.", game) }; } else { return listOfReviews.ToArray(); } } [OperationContract] [WebInvoke] public void AddReview(string game, string comment) { reviews[game].Add(comment); } [OperationContract] [WebInvoke] public void ClearReviews(string game) { reviews[game].Clear(); } } }
我们选择在因特网信息服务(IIS)中寄宿这个服务。列表13.8 显示了用来寄宿服务的GameReviewService.svc。
列表13.8 GameReviewService.svc
<%@ ServiceHost Language="C#" Debug="true" Service="EssentialWCF.GameReviewService" CodeBehind="GameReviewService.cs" Factory="System.ServiceModel.Activation.WebScriptServiceHostFactory" %>
列表13.9 显示了用户寄宿GameReviewService的配置信息。配置信息最重要的方面是使用webHttpBinding绑定和enableWebScript终结点行为。这开启了使用JSON并使用ASP.NET 为GameReviewService生成必要的客户端代理代码。
列表13.9 web.config
<system.serviceModel> <serviceHostingEnvironment aspNetCompatibilityEnabled="true" /> <services> <service behaviorConfiguration="MetadataBehavior" name="EssentialWCF.GameReviewService"> <endpoint address="" binding="webHttpBinding" contract="EssentialWCF.GameReviewService" /> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" /> </service> </services> <behaviors> <endpointBehaviors> <behavior name="AjaxBehavior"> <enableWebScript /> </behavior> <behavior name="XBOX360GameReview.Service1AspNetAjaxBehavior"> <enableWebScript /> </behavior> </endpointBehaviors> <serviceBehaviors> <behavior name="MetadataBehavior"> <serviceMetadata httpGetEnabled="true" /> <serviceDebug includeExceptionDetailInFaults="false" /> </behavior> </serviceBehaviors> </behaviors> </system.serviceModel>
你可以通过使用ASP.NET ScriptManager向服务添加一个引用来配置GameReviewService 被ASP.NET 使用。列表13.10 显示了标记用来引用GameReviewService。在这个场景后面生成了一个引用一个JavaScript文件和客户端代理的客户端脚本。对我们的例子来说,客户端JavaScript的URI是http://localhost/GameReviewService/GameReviewService.svc/js.
列表13.10 使用ASP.NET ScriptManager引用服务
<asp:ScriptManager ID="ScriptManager1" runat="server"> <Services> <asp:ServiceReference Path="~/GameReviewService.svc"/> </Services> </asp:ScriptManager>
我们已经包含了用来创建XBOX 360 游戏审查网络应用程序的ASP.NET web 窗体。这显示了服务如何从客户端脚本被调用以及结果如何被动态地显示到控制界面上。
列表13.11 进行客户端代理调用
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="EssentialWCF._Default" %> <%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="cc1" %> <!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 id="Head1" runat="server"> <title>XBOX 360 Game Reviews</title> <script type="text/javascript"> function pageLoad() { } </script> </head> <body> <form id="form1" runat="server"> <div> <asp:ScriptManager ID="ScriptManager1" runat="server"> <Services> <asp:ServiceReference Path="~/GameReviewService.svc"/> </Services> </asp:ScriptManager> <script type="text/javascript"> EssentialWCF.GameReviewService.set_defaultFailedCallback(OnError); function ListGames() { EssentialWCF.GameReviewService.Games(OnListGamesComplete); } function ListReviews() { var gameListBox = document.getElementById("GameListBox"); EssentialWCF.GameReviewService.Reviews(gameListBox.value, OnListReviewsComplete); } function AddReview() { var gameListBox = document.getElementById("GameListBox"); var reviewTextBox = document.getElementById("ReviewTextBox"); EssentialWCF.GameReviewService.AddReview(gameListBox.value, reviewTextBox.value, OnUpdateReviews); } function ClearReviews() { var gameListBox = document.getElementById("GameListBox"); EssentialWCF.GameReviewService.ClearReviews(gameListBox.value, OnUpdateReviews); } function OnListGamesComplete(result) { var gameListBox = document.getElementById("GameListBox"); ClearAndSetListBoxItems(gameListBox, result); } function OnListReviewsComplete(result) { var reviewListBox = document.getElementById("ReviewListBox"); ClearAndSetListBoxItems(reviewListBox, result); } function OnUpdateReviews(result) { ListReivews(); } function ClearAndSetListBoxItems(listBox, games) { for (var i = listBox.options.length - 1; i > -1; i--) { listBox.options[i] = null; } var textValue; var optionItem; for (var j = 0; j < games.length; j++) { textValue = games[j]; optionItem = new Option(textValue, textValue, false, false); listBox.options[listBox.length] = optionItem; } } function OnError(result) { alert("Error: " + result.get_message()); } function OnLoad() { ListGames(); var gameListBox = document.getElementById("GameListBox"); if (gameListBox.attachEvent) { gameListBox.attachEvent("onchange", ListReviews); } else { gameListBox.attachEventListener("change", ListReviews, false); } } Sys.Application.add_load(OnLoad); </script> <h1>XBOX 360 Game Review</h1> <table> <tr style="height:250px;vertical-align:top;"><td style="240px">Select a game:<br /><asp:ListBox ID="GameListBox" runat="server" Width="100%"></asp:ListBox></td> <td style="400px">Commnets:<br /><asp:ListBox ID="ReviewListBox" runat="server" Width="100%" Height="100%"></asp:ListBox></td></tr> <tr style="vertical-align:top;"><td colspan="2"> Enter a comment:<br/> <asp:TextBox ID="ReviewTextBox" runat="server" Width="400px"></asp:TextBox> <input id="AddReviewButton" type="button" value="Add" onclick="AddReview();" /> <input id="ClearReviewButtion" type="button" value="Clear" onclick="ClearReviews();" /> </tr> </table> </div> </form> </body> </html>