使用 HTML5、CSS3 和 JQuery 为自托管 WCF 服务开发 Web 2.0 用户界面






4.08/5 (8投票s)
为自托管 WCF 服务使用 HTML5、CSS3 和 JQuery 开发 Web 2.0 用户界面

引言
本文介绍了一种利用自托管 WCF 服务来提供 Web 2.0 用户界面以及 Web 服务的方法。
通过使用此方法,可以使用 HTML5、CSS3 等 Web 2.0 技术构建工具或应用程序,并以自托管 WCF 服务作为后端业务层。因此,开发人员可以利用 JQuery 或 Underscore 等高级 JavaScript 库来构建用户界面。这消除了在客户端计算机上安装 .NET Framework 的需要。
传统上,自托管 WCF 服务使用 WinForms 和 WPF 技术构建用户界面。为了能够使用浏览器作为 UI 平台,ASP.NET 和 IIS 成为硬性依赖。对于通常在单个计算机或内网中运行且用户数量有限的工具和应用程序来说,IIS 被认为是过度配置。因此,以浏览器作为 UI 平台,结合 HTML5、CSS3,并由强大的 JavaScript 引擎提供支持,是自托管 WCF 服务的一个非常有前景的选择。
背景
WCF 的早期版本只能处理 SOAP 消息。从 .NET 3.5 开始,使用 `WebHttpBinding` 等绑定,WCF 能够直接支持从 JavaScript 的 Ajax 调用中消费 Web 服务。然而,消费 JSON 等数据格式仍然不是开箱即用的体验。为每个参数和返回类型编写 `DataContract` 的工作量相当大。对于 XML 等其他数据格式,由于需要额外一步将 XML 转换为 JSON,客户端无法直接受益于 JavaScript 库。
此方法将 'Stream
' 视为输入,并将 'out
' 类型用于 UI 接口操作。它还支持基本身份验证,可以扩展用于高级用法。并且不对任何层进行自定义,而是直接利用 .NET 3.5 中 WCF 的开箱即用功能。这也可以与 .NET 4.0 一起使用。但是,它使用了 JSON.Net 来格式化 json 对象。
Using the Code
下载并构建源代码。您可以使用 Visual Studio 2010 或 VS 命令行。(代码是为 .NET 4.0 构建的,但也可以与 .NET 3.5 一起使用。)
- 启动 `WCFWebApp.exe`。如果端口 2011 不可用,请更改为其他端口。并且您需要管理员权限。
- 当服务器运行时,打开任何支持 JavaScript 的浏览器,然后导航到“http:/
: ”。/index.htm - 输入用户名“user”和密码“pass”,然后单击登录。您将登录,然后会加载一个关于页面。
- 单击“States”链接。
- 勾选任何状态的“visited”选项,然后单击“Update”。
- 将页面切换到“About”,然后再返回到“States”,并观察最后一个选择已保留。
- 单击右上角的“Logout”。
所有这些操作都是通过自托管的 WCF 服务运行的。这演示了身份验证、获取静态文件、获取以及设置数据等功能。以下各节将详细介绍代码。
服务器代码
主类仅使用 `WebHttpBinding` 和 `WebServiceHost` 启动一个 WCF 服务。
class Program
{
static void Main(string[] args)
{
string baseAddress = "http://" + Environment.MachineName + ":2011/";
using (WebServiceHost host =
new WebServiceHost(typeof(WebApp), new Uri(baseAddress)))
{
WebHttpBinding binding = new WebHttpBinding();
host.AddServiceEndpoint(typeof
(IWCFWebApp01), binding, "").Behaviors.Add(new WebHttpBehavior());
host.Open();
... other lines left for brevity
}
}
}
服务合同定义了一个名为 'Files
' 的方法来提供所有 `static` HTML 文件,另一个名为 'Links
' 的方法提供所有链接文件,如 JavaScript、样式表和数据。其他资源,如 login、logout、States 和 State,是服务操作。这里值得注意的一点是用于输入和输出的 'Stream
' 数据类型。
[ServiceContract]
public interface IWCFWebApp01
{
[OperationContract, WebGet(UriTemplate = "/{resource}.{extension}")]
Stream Files(string resource, string extension);
[OperationContract, WebGet(UriTemplate = "/{path}/{resource}.{extension}")]
Stream Links(string path, string resource, string extension);
[OperationContract, WebInvoke(Method = "POST", UriTemplate = "/login")]
Stream Login(Stream request);
[OperationContract, WebInvoke(Method = "POST", UriTemplate = "/logout")]
Stream Logout(Stream request);
[OperationContract, WebGet(UriTemplate = "/states")]
Stream States();
[OperationContract, WebInvoke(Method = "POST", UriTemplate = "/state")]
Stream State(Stream request);
}
现在来看服务实现。由于此方法主要用于自托管 WCF 服务,因此具有并发线程的单例实例就足够了。可以考虑使用会话。但与 IIS 托管的服务不同,自托管服务通常服务用户数量有限,因此默认的并发性就足够了。功能方面,构造函数只是将数据加载到本地成员中。
[ServiceBehavior(InstanceContextMode =
InstanceContextMode.Single,ConcurrencyMode=ConcurrencyMode.Multiple)]
public class WebApp : IWCFWebApp01
{
JObject states;
public WebApp()
{
if (states==null)
states = JObject.Parse(File.ReadAllText("web\\data\\states.json"));
}
... other lines left for brevity
}
现在服务器正在运行,当用户首次尝试访问时,将提供几个 HTM、CSS 和 JavaScript 文件。这些文件由 'Files
' 和 'Links
' 方法处理。链接是 `index.htm` 中头部区域引用的文件,例如 JQuery。在 'Files
' 方法中,根据扩展名从不同的文件夹中选择不同类型的文件。可以根据文件类型扩展 `Switch` 语句。
public Stream Links(string path, string resource, string extension)
{
... other lines left for brevity
}
public Stream Files(string resource, string extension)
{
switch (extension)
{
case "htm":
... other lines left for brevity
case "js":
... other lines left for brevity
}
}
当用户发出登录请求时,基本身份验证令牌会通过标准的“Authorization
”头部发送。这将在稍后描述的单独方法 'Authenticate
' 中进行验证。用户名也作为 JSON 对象发送在请求流中,并使用 JSON.Net 库解析为 JSON 对象。Logout 方法与 login 类似。
public Stream Login(Stream request)
{
if (!Authenticate()) return null;
... other lines left for brevity
JObject o = JObject.Parse(data);
}
当用户单击 'States
' 时,请求将到达以下方法。由于此资源没有扩展名,请求将不会通过 'Files
' 方法。此处会对请求进行身份验证,并从成员变量发送数据。
public Stream States()
{
if (!Authenticate()) return null;
WebOperationContext.Current.OutgoingResponse.ContentType = "application/json";
return new MemoryStream(Encoding.ASCII.GetBytes(states.ToString()),false);
}
当用户进行修改并单击 'Update
' 时,将调用以下方法。此方法解析状态 ID,更新类成员变量,并将更新后的列表返回给客户端。
public Stream State(Stream request)
{
... other lines left for brevity
JObject data = JObject.Parse(new string(buffer));
int id = ((int)data["id"]) -1;
states["states"][id]["visited"] = true;
return States();
}
需要授权的身份验证方法会调用以下方法
public bool Authenticate()
{
string userName = "user";
string password = "pass";
string basicAuthCode = Convert.ToBase64String
(Encoding.ASCII.GetBytes (string. Format ("{0}: {1}", userName, password)));
string token = WebOperationContext.Current.IncomingRequest.Headers["Authorization"];
if (token.Contains(basicAuthCode))
{
return true;
}
else
{
WebOperationContext.Current.OutgoingResponse.StatusCode =
HttpStatusCode.Unauthorized;
return false;
}
}
客户端代码
客户端代码放置在名为 'web' 的单独文件夹中。在此文件夹的根目录下,放置了所有 `static` HTM 文件。并且为图像、JavaScript 和样式表包含了单独的子文件夹。这些文件会根据扩展名,由服务器代码中的 'Files
' 方法引用。
客户端遵循 单页应用程序 设计。因此,只有 'index.htm' 是一个完整的 HTML 页面。其他 HTML 文件会通过 Ajax 调用填充到 'content
' 区域,如下所示(以 states 为例)
function StatesPage () {
this.loadStatesPage = function (data) {
content = undefined;
$.each (data, function (index, value) {
if (data[index]["id"]=="content") {
content = data[index].innerHTML;
$("#content")[0].innerHTML = content;
$("#b_update")[0].onclick = updateStates;
loadStatesTable();
}
});
if (content == undefined) {alert("Failed to load page: Content missing"); return;}
}
... other lines left for brevity
}
身份验证:客户端身份验证令牌在 login 类中。登录后,此令牌会在每次调用的 'beforeSend
' 函数中添加到头部。客户端的其他代码需要了解 Jquery、JavaScript 和 Ajax 概念,这些概念在网上都有很好的解释。
关注点
如果需要 Windows 身份验证,可以自定义服务主机。
使用更结构化的 JavaScript 库和 MVC 架构,无需更改服务器端代码即可实现。
考虑使用 JQuery UI 插件来实现通用的外观和感觉。
由于 UI 是基于浏览器的,因此为手持设备扩展 UI 变得非常容易。
历史
- 2011-07-22:第一个版本
- 2011-07-23:文章已更新