[转载]反向Ajax,第1部分:Comet介绍

mikel阅读(1180)

[转载]反向Ajax,第1部分:Comet介绍_知识库_博客园.

前言

web开发在过去的几年中有了很大的进展,我们已经远超了把静态网页链接在一起的做法,这种做法会引起浏览器的刷新,并且要等待页面的加载。现 在需要的是能够通过web来访问的完全动态的应用,这些应用通常需要尽可能的快,提供近乎实时的组件。在这一新的由五部分组成的文章系列中,我们学习如何 使用反向Ajax(Reverse Ajax)技术来开发事件驱动的web应用。

在这第一篇文章中,我们要了解反向Ajax、轮询(polling)、流(streaming)、Comet和长轮询(long polling),学习如何实现不同的反向Ajax通信技术,并探讨每种方法的优点和缺点。你可以下载本文中例子的相应源代码。

Ajax、反向Ajax和WebSocket

异步的JavaScript和XML(Asynchronous JavaScript and XML,Ajax),一种可通过JavaScript来访问的浏览器功能特性,其允许脚本向幕后的网站发送一个HTTP请求而又无需重新加载页面。 Ajax的出现已经超过了十年,尽管其名字中包含了XML,但你几乎可以在Ajax请求中传送任何的东西,最常用的数据是JSON,其与 JavaScript语法很接近,且消耗更少带宽。清单1给出了这样的一个例子,Ajax请求通过某个地方的邮政编码来检索该地的名称。

清单1. Ajax请求举例

var url =http://www.geonames.org/postalCodeLookupJSON?postalcode=
+ $(#postalCode).val() +&country=
+ $(#country).val() +&callback=?;
$.getJSON(url,
function(data) {
$(
#placeName).val(data.postalcodes[0].placeName);
});

在本文可下载的源代码中,你可在listing1.html中看到这一例子的作用。

反向Ajax(Reverse Ajax)本质上则是这样的一种概念:能够从服务器端向客户端发送数据。在一个标准的HTTP Ajax请求中,数据是发送给服务器端的,反向Ajax可以某些特定的方式来模拟发出一个Ajax请求,这些方式本文都会论及,这样的话,服务器就可以尽 可能快地向客户端发送事件(低延迟通信)。

WebSocket技术来自HTML5,是一种最近才出现的技术,许多浏览器已经支持它(Firefox、Google Chrome、Safari等等)。WebSocket启用双向的、全双工的通信信道,其通过某种被称为WebSocket握手的HTTP请求来打开连 接,并用到了一些特殊的报头。连接保持在活动状态,你可以用JavaScript来写和接收数据,就像是正在用一个原始的TCP套接口一样。 WebSocket会在这一文章系列的第二部分中谈及。

反向Ajax技术

反向Ajax的目的是允许服务器端向客户端推送信息。Ajax请求在缺省情况下是无状态的,且只能从客户端向服务器端发出请求。你可以通过使用技术模拟服务器端和客户端之间的响应式通信来绕过这一限制。

HTTP轮询和JSONP轮询

轮询(polling)涉及了从客户端向服务器端发出请求以获取一些数据,这显然就是一个纯粹的Ajax HTTP请求。为了尽快地获得服务器端事件,轮询的间隔(两次请求相隔的时间)必须尽可能地小。但有这样的一个缺点存在:如果间隔减小的话,客户端浏览器 就会发出更多的请求,这些请求中的许多都不会返回任何有用的数据,而这将会白白地浪费掉带宽和处理资源。

图1中的时间线说明了客户端发出了某些轮询请求,但没有信息返回这种情况,客户端必须要等到下一个轮询来获取两个服务器端接收到的事件。

图1. 使用HTTP轮询的反向Ajax

JSONP轮询基本上与HTTP轮询一样,不同之处则是JSONP可以发出跨域请求(不是在你的域内的请求)。清单1使用JSONP来通过邮政编码获取地名,JSONP请求通常可通过它的回调参数和返回内容识别出来,这些内容是可执行的JavaScript代码。

要在JavaScript中实现轮询的话,你可以使用setInterval来定期地发出Ajax请求,如清单2所示:

清单2. JavaScript轮询

setInterval(function() {
$.getJSON(
events, function(events) {
console.log(events);
});
},
2000);

文章源代码中的轮询演示给出了轮询方法所消耗的带宽,间隔很小,但可以看到有些请求并未返回事件,清单3给出了这一轮询示例的输出。

清单3. 轮询演示例子的输出

[client] checking for events…
[client] no event
[client] checking for events…
[client]2 events
[event] At Sun Jun 0515:17:14 EDT 2011
[event] At Sun Jun 0515:17:14 EDT 2011
[client] checking for events…
[client]1 events
[event] At Sun Jun 0515:17:16 EDT 2011

用JavaScript实现的轮询的优点和缺点:

1. 优点:很容易实现,不需要任何服务器端的特定功能,且在所有的浏览器上都能工作。

2. 缺点:这种方法很少被用到,因为它是完全不具伸缩性的。试想一下,在100个客户端每个都发出2秒钟的轮询请求的情况下,所损失的带宽和资源数量,在这种情况下30%的请求没有返回数据。

Piggyback

捎带轮询(piggyback polling)是一种比轮询更加聪明的做法,因为它会删除掉所有非必需的请求(没有返回数据的那些)。不存在时间间隔,客户端在需要的时候向服务器端发 送请求。不同之处在于响应的那部分上,响应被分成两个部分:对请求数据的响应和对服务器事件的响应,如果任何一部分有发生的话。图2给出了一个例子。

图2. 使用了piggyback轮询的反向Ajax

在实现piggyback技术时,通常针对服务器端的所有Ajax请求可能会返回一个混合的响应,文章的下载中有一个实现示例,如下面的清单4所示。

清单4. piggyback代码示例

$(#submit).click(function() {
$.post(
ajax, function(data) {
var valid = data.formValid;
// 处理验证结果
//
然后处理响应的其他部分(事件)
processEvents(data.events);
});
});

清单5给出了一些piggyback输出。

清单5. piggyback输出示例

[client] checking for events…
[server] form valid ? true
[client]4 events
[event] At Sun Jun 0516:08:32 EDT 2011
[event] At Sun Jun 0516:08:34 EDT 2011
[event] At Sun Jun 0516:08:34 EDT 2011
[event] At Sun Jun 0516:08:37 EDT 2011

你可以看到表单验证的结果和附加到响应上的事件,同样,这种方法也有着一些优点和缺点:

1. 优点:没有不返回数据的请求,因为客户端对何时发送请求做了控制,对资源的消耗较少。该方法也是可用在所有的浏览器上,不需要服务器端的特殊功能。

2. 缺点:当累积在服务器端的事件需要传送给客户端时,你却一点都不知道,因为这需要一个客户端行为来请求它们。

Comet

使用了轮询或是捎带的反向Ajax非常受限:其不具伸缩性,不提供低延迟通信(只要事件一到达服务器端,它们就以尽可能快的速度到达浏览器 端)。Comet是一个web应用模型,在该模型中,请求被发送到服务器端并保持一个很长的存活期,直到超时或是有服务器端事件发生。在该请求完成后,另 一个长生存期的Ajax请求就被送去等待另一个服务器端事件。使用Comet的话,web服务器就可以在无需显式请求的情况下向客户端发送数据。

Comet的一大优点是,每个客户端始终都有一个向服务器端打开的通信链路。服务器端可以通过在事件到来时立即提交(完成)响应来把事件推给客 户端,或者它甚至可以累积再连续发送。因为请求长时间保持打开的状态,故服务器端需要特别的功能来处理所有的这些长生存期请求。图3给出了一个例子。(这 一文章系列的第2部分会更加详细地解释服务器端的约束条件)。

图3. 使用Comet的反向Ajax

Comet的实现可以分成两类:使用流(streaming)的那些和使用长轮询(long polling)的那些。

使用HTTP流的Comet

在流(streaming)模式中,有一个持久连接会被打开。只会存在一个长生存期请求(图3中的#1),因为每个到达服务器端的事件都会通过 这同一连接来发送。因此,客户端需要有一种方法来把通过这同一连接发送过来的不同响应分隔开来。从技术上来讲,两种常见的流技术包括Forever Iframe(隐藏的IFrame),或是被用来在JavaScript中创建Ajax请求的XMLHttpRequest对象的多部分(multi- part)特性。

Forever Iframe

Forever Iframe(永存的Iframe)技术涉及了一个置于页面中的隐藏Iframe标签,该标签的src属性指向返回服务器端事件的servlet路径。每 次在事件到达时,servlet写入并刷新一个新的script标签,该标签内部带有JavaScript代码,iframe的内容被附加上这一 script标签,标签中的内容就会得到执行。

1. 优点:实现简单,在所有支持iframe的浏览器上都可用。

2. 缺点: 没有方法可用来实现可靠的错误处理或是跟踪连接的状态,因为所有的连接和数据都是由浏览器通过HTML标签来处理的,因此你没有办法知道连接何时在哪一端已被断开了。

Multi-part XMLHttpRequest

第二种技术,更可靠一些,是XMLHttpRequest对象上使用某些浏览器(比如说Firefox)支持的multi-part标志。 Ajax请求被发送给服务器端并保持打开状态,每次有事件到来时,一个多部分的响应就会通过这同一连接来写入,清单6给出了一个例子。

清单6. 设置Multi-part XMLHttpRequest的JavaScript代码示例

var xhr = $.ajaxSettings.xhr();
xhr.multipart
=true;
xhr.open(
GET, ajax, true);
xhr.onreadystatechange
= function() {
if (xhr.readyState == 4) {
processEvents($.parseJSON(xhr.responseText));
}
};
xhr.send(
null);

在服务器端,事情要稍加复杂一些。首先你必须要设置多部分请求,然后挂起连接。清单7展示了如何挂起一个HTTP流请求。(这一系列的第3部分会更加详细地谈及这些API。)

清单7. 使用Servlet 3 API来在servlet中挂起一个HTTP流请求

protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 开始请求的挂起
AsyncContext asyncContext = req.startAsync();
asyncContext.setTimeout(
0);

// 给客户端发回多部分的分隔符
resp.setContentType(multipart/x-mixed-replace;boundary=\”
+ boundary +\”);
resp.setHeader(
Connection, keep-alive);
resp.getOutputStream().print(
+ boundary);
resp.flushBuffer();

// 把异步上下文放在列表中以被将来只用
asyncContexts.offer(asyncContext);
}

现在,每次有事件发生时你都可以遍历所有的挂起连接并向它们写入数据,如清单8所示:

清单8. 使用Servlet 3 API来向挂起的多部分请求发送事件

for (AsyncContext asyncContext : asyncContexts) {
HttpServletResponse peer
= (HttpServletResponse)
asyncContext.getResponse();
peer.getOutputStream().println(
Content-Type: application/json);
peer.getOutputStream().println();
peer.getOutputStream().println(
new JSONArray()
.put(
At +new Date()).toString());
peer.getOutputStream().println(
+ boundary);
peer.flushBuffer();
}

本文可下载文件的Comet-straming文件夹中的部分说明了HTTP流,在运行例子并打开主页时,你会看到只要事件一到达服务器端,虽 然不同步但它们几乎立刻会出现在页面上。而且,如果打开Firebug控制台的话,你就能看到只有一个Ajax请求是打开的。如果再往下看一些,你会看到 JSON响应被附在Response选项卡中,如图4所示:

图4. HTTP流请求的FireBug视图

照例,做法存在着一些优点和缺点:

1. 优点:只打开了一个持久连接,这就是节省了大部分带宽使用率的Comet技术。

2. 缺点:并非所有的浏览器都支持multi-part标志。某些被广泛使用的库,比如说用Java实现的CometD,被报告在缓冲方面有问题。例如,一些 数据块(多个部分)可能被缓冲,然后只有在连接完成或是缓冲区已满时才被发送,而这有可能会带来比预期要高的延迟。

使用HTTP长轮询的Comet

长轮询(long polling)模式涉及了打开连接的技术。连接由服务器端保持着打开的状态,只要一有事件发生,响应就会被提交,然后连接关闭。接下来。一个新的长轮询连接就会被正在等待新事件到达的客户端重新打开。

你可以使用script标签或是单纯的XMLHttpRequest对象来实现HTTP长轮询。

script标签

正如iframe一样,其目标是把script标签附加到页面上以让脚本执行。服务器端则会:挂起连接直到有事件发生,接着把脚本内容发送回浏览器,然后重新打开另一个script标签来获取下一个事件。

1. 优点:因为是基于HTML标签的,所有这一技术非常容易实现,且可跨域工作(缺省情况下,XMLHttpRequest不允许向其他域或是子域发送请求)。

2. 缺点:类似于iframe技术,错误处理缺失,你不能获得连接的状态或是有干涉连接的能力。

XMLHttpRequest长轮询

第二种,也是一种推荐的实现Comet的做法是打开一个到服务器端的Ajax请求然后等待响应。服务器端需要一些特定的功能来允许请求被挂起, 只要一有事件发生,服务器端就会在挂起的请求中送回响应并关闭该请求,完全就像是你关闭了servlet响应的输出流。然后客户端就会使用这一响应并打开 一个新的到服务器端的长生存期的Ajax请求,如清单9所示:

清单9. 设置长轮询请求的JavaScript代码示例

function long_polling() {
$.getJSON(
ajax, function(events) {
processEvents(events);
long_polling();
});
}
long_polling();

在后端,代码也是使用Servlet 3 API来挂起请求,正如HTTP流的做法一样,但你不需要所有的多部分处理代码,清单10给出了一个例子。

清单10. 挂起一个长轮询Ajax请求

protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
AsyncContext asyncContext
= req.startAsync();
asyncContext.setTimeout(
0);
asyncContexts.offer(asyncContext);
}

在接收到事件时,只是取出所有的挂起请求并完成它们,如清单11所示:

清单11. 在有事件发生时完成长轮询Ajax请求

while (!asyncContexts.isEmpty()) {
AsyncContext asyncContext
= asyncContexts.poll();
HttpServletResponse peer
= (HttpServletResponse)
asyncContext.getResponse();
peer.getWriter().write(
new JSONArray().put(At + new Date()).toString());
peer.setStatus(HttpServletResponse.SC_OK);
peer.setContentType(
application/json);
asyncContext.complete();
}

在附带的下载源文件中,comet-long-polling文件夹包含了一个长轮询示例web应用,你可以使用 mvn jetty:run 命令来运行它。

1. 优点:客户端很容易实现良好的错误处理系统和超时管理。这一可靠的技术还允许在与服务器端的连接之间有一个往返,即使连接是非持久的(当你的应用有许多的 客户端时,这是一件好事)。它可用在所有的浏览器上;你只需要确保所用的XMLHttpRequest对象发送到的简单的Ajax请求就可以了。

2. 缺点:相比于其他技术来说,不存在什么重要的缺点,像所有我们已经讨论过的技术一样,该方法依然依赖于无状态的HTTP连接,其要求服务器端有特殊的功能来临时挂起连接。

建议

因为所有现代的浏览器都支持跨域资源共享(Cross-Origin Resource Share,CORS)规范,该规范允许XHR执行跨域请求,因此基于脚本的和基于iframe的技术已成为了一种过时的需要。

把Comet做为反向Ajax的实现和使用的最好方式是通过XMLHttpRequest对象,该做法提供了一个真正的连接句柄和错误处理。考 虑到不是所有的浏览器都支持multi-part标志,且多部分流可能会遇到缓冲问题,因此建议你选择经由HTTP长轮询使用 XMLHttpRequest对象(在服务器端挂起的一个简单的Ajax请求)的Comet模式,所有支持Ajax的浏览器也都支持该种做法。

结论

本文提供的是反向Ajax技术的一个入门级介绍,文章探索了实现反向Ajax通信的不同方法,并说明了每种实现的优势和弊端。你的具体情况和应 用需求将会影响到你对最合适方法的选择。不过一般来说,如果你想要在低延迟通信、超时和错误检测、简易性,以及所有浏览器和平台的良好支持这几方面有一个 最好的折中的话,那就选择使用了Ajax长轮询请求的Comet。

请继续阅读这一系列的第2部分:该部分 将会探讨第三种反向Ajax技术:WebSocket。尽管还不是所有的浏览器都支持该技术,但WebSocket肯定是一种非常好的反向Ajax通信媒 介,WebSocket消除了所有与HTTP连接的无状态特性相关的限制。第2部分还会谈及由Comet和WebSocket技术带来的服务器端约束。

代码下载

reverse_ajaxpt1_source.zip

参考资料

学习资料

1. 在维基百科上了解这些内容:

1.1 Ajax
1.2 Reverse Ajax
1.3 Comet
1.4 WebSockets

2. “Exploring Reverse AJAX”(Google Maps .Net Control博客,2006年8月):获得一些关于反向Ajax技术的介绍说明。

3. “Cross-domain communications with JSONP, Part 1: Combine JSONP and jQuery to quickly build powerful mashups”(developerWorks, February 2009):了解如何把不起眼的跨域调用技术(JSONP)和一个灵活的JavaScript库(JQuery)结合在一起,以令人惊讶的速度构建出一些功能强大的聚合应用。

4. “Cross-Origin Resource Sharing (CORS)”规范(W3C, July 2010):了解更多关于这一机制的内容,该机制允许XHR执行跨域请求。

5. “Build Ajax applications with Ext JS”(developerWorks, July 2008):对大大增强了JavaScript开发的这一框架有一个大概的了解。

6. “Mastering Ajax, Part 2: Make asynchronous requests with JavaScript and Ajax”(developerWorks, January 2006):学习如何使用Ajax和XMLHttpRequest对象来创建一种永不会让用户等待服务器响应的请求/响应模型。

7. “Create Ajax applications for the mobile Web”(developerWorks, March 2010):了解如何使用Ajax构建跨浏览器的智能手机Web应用。

8. “Improve the performance of Web 2.0 applications“(developerWorks, December 2009):探讨不同的浏览器端缓存机制。

9. Introducing JSON”(JSON.org):获得对JSON语法的一个入门介绍。

10. developerWorks Web development zone:获得各种谈论基于Web的解决方案的文章。

11. developerWorks podcasts:收听各种与软件开发者进行的有趣的访谈和讨论。

12. developerWorks technical events and webcasts:随时关注developerWorks的技术事件和webcast的进展。

获取产品和技术

1. 获取ExtJS,这是一个用来构建富互联网应用的跨浏览器JavaScript库。

2. XAMPP为Apache、PHP、MySQL和其他产品提供了一种简易的安装。

3. 免费试用IBM软件,下载使用版,登录在线试用,在沙箱环境中使用产品,或是通过云来访问,有超过100种IBM产品试用版选择。

讨论

1. 现在就创建你的developerWorks个人资料,并设置一个关于Reverse Ajax的观看列表。与developerWorks社区建立联系并保持联系。

2. 找到其他在web开发方面感兴趣的developerWorks成员

3. 分享你的知识:加入一个关注web专题的developerWorks组

4. Roland Barcia在他的博客中谈论Web 2.0和中间件

5. 关注developerWork成员的shared bookmarks on web topics

6. 快速获得答案:访问Web 2.0 Apps论坛

7. 快速获得答案:访问Ajax论坛

关于作者

Mathieu Carbou是Ovea的一位提供服务和开发解决方案的Java web架构师和顾问。他是几个开源项目的提交者和领导者,也是Montreal的Java User Group的一位演讲者和领导者。Mathieu有着很强的代码设计和最佳实践背景,他是一个从客户端到后端的事件驱动的web开发方面的专家。他的工作 重点是在高度可伸缩的web应用中提供事件驱动的和消息式的解决方案。你可以看一看他的博客

相关博文

用 ASP.NET MVC 实现基于 Multipart XMLHttpRequest 的 Comet

用 ASP.NET MVC 实现基于 XMLHttpRequest long polling(长轮询) 的 Comet

[转载]用 ASP.NET MVC 实现基于 Multipart XMLHttpRequest 的 Comet

mikel阅读(1052)

[转载]用 ASP.NET MVC 实现基于 Multipart XMLHttpRequest 的 Comet – dudu – 博客园.

Comet是什么?

英文解释:Comet is a web application model in which a long-held HTTP request allows a web server to push data to a browser, without the browser explicitly requesting it. (来自wiki

中文解释:Comet是基于 HTTP 长连接的“服务器推”技术(来自IBM developerWorks)。

Multipart XMLHttpRequest是什么?

英文解释:It is a method for bundling multiple HTTP requests into a single HTTP request and unbundling on the client side through a JavaScript handler. (来自TechnoMagicians Blog

中文解决:它允许客户端只用一个HTTP请求就可以从服务端向客户端传送多个资源(来自鼓浪鱼-前端博客)。

为什么要用 ASP.NET MVC 实现?

昨天学习了“反向Ajax,第1部分:Comet介绍”(英文版),但文中的代码服务端代码用的是Java。为了更好的理解这部分知识,实际体验一下,于是用ASP.NET MVC实现了其中一个示例 —— 基于 Multi-part XMLHttpRequest 的 Comet。

这东西有实用价值吗?

说实话,没有实用价值,只为学习之用。因为支持Multipart XMLHttpRequest的浏览器太少,目前只知道FireFox支持,Chrome与IE9都不支持。

代码示例

1. 前端JavaScript代码

var xhr = $.ajaxSettings.xhr();
xhr.multipart = true;
xhr.open('GET', '/comet/multipart', true);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
$('#logs').append(xhr.responseText+"
");
}
};
xhr.send(null);

关键代码就是xhr.multipart = true;

2. 服务端ASP.NET MVC 控制器代码

public class CometController : Controller
{
//结束标志,这是随机生成的
static string Boundary = "ABCDEFGHIJKLMNOPQRST";

public ActionResult Multipart()
{
Response.ContentType = "multipart/x-mixed-replace;boundary=\"" + Boundary + "\"";
Response.Headers.Add("Connection", "keep-alive");
Response.Output.Write("--" + Boundary);
Response.Flush();
//每隔5秒种向客户端发送一次数据
while (true)
{
//发送给客户端的数据的MIME类型,如果是JSON,就用application/json
//注意这里一定要用WriteLine()
Response.Output.WriteLine("Content-Type: plain/text");
//这句生成空行的代码不能少
Response.Output.WriteLine();
Response.Output.WriteLine(DateTime.Now.ToString("HH:mm:ss.FFF"));
//发送结束标志,客户端就知道完成了一次发送
Response.Output.WriteLine("--" + Boundary);
Response.Flush();
System.Threading.Thread.Sleep(5000);
}
}
}

上面这段代码虽然看起来简单,但当时调试时还是费了一些周折。

代码下载

CometMvcDemo.rar

需要在IIS中以集成模式运行该程序(以VS2010内置Web服务器运行会报错)。访问MultipartXhr.htm,会5秒钟显示一次服务器时间。

[转载]Notify - JQuery消息通知插件

mikel阅读(1154)

[转载]Notify – 消息通知插件 – 孟晨 – 博客园.

Notify – 消息通知插件

1) Sticky是一个简单的页面消息提醒JQuery插件。可以设置让消息在页面的四个角落出现,也可以设置在页面中央出现。可以手动关闭消息,也可以设置成自动关闭
Sticky
2)JNotify是一个JQuery插件,基于jQuery UI实现,用于创建页面消息提醒状态提示条
JNotify
3)Humane JS是一个不依赖任何框架的轻巧的信息提示插件
Humane JS

4)jQuery.UI MessengerOutlook like message notification Widget,这个jQuery.UI控件类似于MessengerOutlook消息弹出提醒框

5)Roar是一个可以在屏幕右下角弹出消息提醒Mootools插件。类似于QQ或MSN右下角弹出消息提示
Roar
6)jGrowl用于制作消息弹出框的jQuery插件。产生效果的类似于Mac OSX系统中Growl事件通知框架
jGrowl

[转载]Web Developer——瑞士军刀一样的FF插件

mikel阅读(1284)

[转载]Web Developer——瑞士军刀一样的FF插件 – 胡尐睿丶 – 博客园.

我的FF一直只装了firebug和一个鼠标手势插件,一是懒得去APP中心淘,二是觉得firebug用着还凑合。

但今天偶尔机会认识了Web Developer,发现它的功能简直碉堡了。

因为功能实在太多了,而且都很实用,我无法一一介绍过来,所以下面我就挑几个我觉得不错的功能进行简单介绍,更多功能就让你们自己去挖掘吧。

查看源码

通常在FF里查看源码都是右键选择查看源代码,如果要查看处理后的源码,只能用firebug来弥补,但是阅读起来着实不便,Web Developer则提供了很好的功能

它分别有三种源码查看,一种就是普通的,也就是原始的,第二种是框架源码,也就是页面里如果嵌套了iframe,通过这个可以直接选择查看,第三种就是刚说的,可以查看处理后的源码。

网页信息

这里功能很多,我就取了一个,“显示区块大小”,这个功能很像firebug里右侧的布局,但它更加直观,直接在页面上显示出来。同时我觉得这功能很适合新手,让他们很快就能掌握盒子模型,当然也适合设计,因为它把区块的宽高都显示出来了。

窗口大小

这个很适合调试网站在不同屏幕分辨率下的效果,比如800*600,同时还支持自定义尺寸。好处就是以后测试不需要修改电脑分辨率了,直接在这里改下尺寸,插件就能把浏览器调整到指定尺寸。

Cookies

这功能不用多介绍了,很方便,不用再去设置里清空了,而且还可以清楚当前会话的cookies,保证其他cookies存在。

……

还多很多功能,总之就是太强大了,真后悔现在才知道这个插件,推荐大家下载:https://addons.mozilla.org/en-US/firefox/addon/web-developer/

我的FF8.0测试通过,兼容性没问题

[转载]ASP.NET性能优化之反向代理缓存

mikel阅读(1106)

[转载]ASP.NET性能优化之反向代理缓存 – 陆敏技 – 博客园.

到目前为止,我们讨论了把缓存存放在ASP.NET的输出缓存中(内存和硬盘),以及浏 览器缓存中,而大型站点的另一种常用做法是将缓存部署在反向代理服务器上,这类缓存我们通常称之为反向代理缓存,比如Squid和Varnish。这两款 软件通常都部署在非WINDOWS平台上,对于Windows平台上的ASP.NET来说,其实一样能使用,我们完全可以把反向代理软件部署在LINUX 上,然后代理会路由到后台的WINDOWS WEB(IIS)服务器。总之,非WINDOWS的世界很精彩。

当然,无论是squid还是varnish都有Windows的扩展版本。本文为了简便起见,基于varnish的Windows版本来描述的。

varnish的官方站点:https://www.varnish-cache.org/

varnish的Windows版本:http://www.cygwin.com/,如果要编译过的直接可用的版本,在这里:http://www.software112.com/products/cygwin-varnish-cache.html

1:将varnish配置为IIS的代理

首先需要为varnish准备配置文件,比如,可以为default.vcl,内容如下:

backend default
{
.host = "192.168.0.77";
.port = "80";
}
sub vcl_fetch
{
remove beresp.http.Set-Cookie;
}
sub vcl_recv
{
remove req.http.Cookie;
}

在我们要示范的这个实例中,这3个配置都不能少,如下,

backend default:指定我们的IIS站点的地址和端口;

sub vcl_fetch:这是一个varnish函数,它varnish从后端服务器,也就是IIS中获得数据后被调用;

sub vcl_recv:varnish函数,表示客户端请求杠杠到达反向代理服务器时被调用;

由于varnish默认在碰到http头中含有Cookie相关标识时直接忽略缓存,所以我们需要上面的两个函数针对Cookie做特殊处理。当然,目前这两个函数只是简单而野蛮的删除标识,实际的应用中我们可能需要根据实际情况为它们加上一些判断条件。

2:启动varnish

下面的命令为我启动varnish:

C:\varnish\bin>varnishd -a :8011 -T :8088 -f c:/varnish/etc/default.vcl -s file,c:/varnish/var/cache,100M

-a:8011表示,让varnish监听在8011端口。由于我测试环境下varnish和iis是在同一台机器上,所以IIS已经占用了80,我这里只有使用其它端口。

-T是为varnish指定一个管理端口;

-f指定所要使用的配置文件;

后面的参数只是让varnish使用文件缓存,大小为100M,当然,应该根据实际情况指定大小;

启动varnish后,如果我们请求http://地址:端口/,就可以等到200OK状态码,那表示varnish已经在正确滴接受请求。

3:一个实例

创建asp.net页面,内容如下:

protected void Page_Load(object sender, EventArgs e)
{
this.Response.AddHeader("Cache-Control", "max-age=60");
this.Response.AddHeader("Last-Modified", DateTime.Now.ToString("U", DateTimeFormatInfo.InvariantInfo));
DateTime IfModifiedSince;
if (DateTime.TryParse(this.Request.Headers.Get("If-Modified-Since"), out IfModifiedSince))
{
if ((DateTime.Now - IfModifiedSince.AddHours(8)).Seconds < 60)
{
Response.Status = "304 Not Modified";
Response.StatusCode = 304;
return;
}
}
string conn = "Data Source=192.168.0.77;Initial Catalog=luminjidb;User Id=sa;Password=sa;";
using (DataSet ds = Common.SQLHelper.ExecuteDataset(conn, CommandType.Text, "select top 1* from NameTb a, DepTb b where a.DepID = b.ID ORDER BY NEWID()"))
{
var result = ds.Tables[0].Rows[0]["name"].ToString();
Response.Write(result);
}
}

对该页面进行压力测试,100个用户,1000个请求,得到的结果如下:

image

如果没有缓存,则结果如下:

image

可以看到吞吐率有非常大的提升。

4:监控varnish

可以使用varnishstat命令,对varnish进行监控,在上面的压力测试中,如果我们使用监控,得到的结果如下:

image

在本例中,我们可以看到共请求了1000次,其中999次命中缓存,那是因为第一次显然肯定是要从IIS中拿输出滴。

5:管理varnish

可以通过多种途径来进行varnish的管理,包括更改配置、停止服务、启动服务、清理缓存等。可以通过varnishadm命令进行管理,如果你是在远程的话,可以使用telnet来进行管理:

telnet 192.168.0.77 8088

其中8088就是我们刚在启动varnish的时候指定的管理端口。连接上之后,stop停止服务、start启动服务,可以敲入help查看所有命令。下面的命令,清除所有缓存:

purge.url *$

6:谨慎引入varnish后带来的缓存变化

引入varnish后,可以发现使用强制刷新(ctrl+R5)后,动态行为发生了改变,即客户端浏览器会去VARNISH上请求数据,但是此时的 缓存中已经存在静态的缓存内容,varnish会首先根据请求的HTTP头去和这个缓存内容判断得出需要是否更新,即由于缓存内容的存在,请求不会去 IIS上进行缓存协商。这个时候,缓存中的静态内容会直接返回给客户端浏览器,这样一来的话,我们在Page_Load中的代码就根本不会执行,因为它是 在IIS中的。

要避免这种情况的发生,我们必须更改VARNISH配置文件,让VARNISH碰到强制更新的时候,忽略缓存,直接去IIS上请求,为配置文件增加如下函数:

sub vcl_hit {
if(req.http.Cache-Control~"no-cache"||req.http.Cache-Control~"max-age=0"||req.http.Pragma~"no-cache"){
set obj.ttl=0s;
return (restart);
}
return (deliver);
}

经过上面的修改后,再次使用强制更新varnish将会忽略缓存,到IIS上去拿正文。

参考:

https://www.varnish-cache.org/docs/trunk/reference/varnishlog.html

https://www.varnish-cache.org/trac/wiki/Introduction#TheVarnishConfigurationLanguage

http://www.docunext.com/wiki/Varnish

http://cd34.com/blog/infrastructure/no-esi-processing-first-char-not/

本系列之前篇:

1:ASP.NET性能优化之构建自定义文件缓存

2:ASP.NET性能优化之让浏览器缓存动态网页

3:ASP.NET性能优化之减少请求

本文首发于AgileSharp-创业互推平台

[转载]ASP.NET中Application_Error对Exception的集中处理

mikel阅读(1004)

[转载]ASP.NET中Application_Error对Exception的集中处理 – 随它去吧 – 博客园.

ASP.NET中对Exception的统一集中处理常见的有两种方式:

1、通过配置Web.config的customErrors节点,配置defaultRedirect属性来实现所有“未处理的异常”均跳转向同一页面

2、通过Global.asax中的Application_Error写代码实现自己想要的操作

但在实际使用当中,往往会出现一些问题,比如Application_Error中的代码未被执行,若使用customErrors那么如何传递错误信息给目标页面等等。

通过我的摸索,总结出一些经验,记录如下:

1、不想通过配置Web.config中customErrors的方法进行页面跳转,而是希望在Application_Error中写代码进行 一些处理然后用Response.Redirect方法跳转到指定页面,这种理论上很不错的方法实际上行不通!Application_Error中写 Response.Redirect来跳转页面不会执行!

2、很多人不想使用配置Web.config实现出错自动跳转的原因无非是:跳转到了一个“死”链接,无法传递当前的错误信息过去,也就没办法进行 相应的处理!其实这是一个误解,任何一个aspx网页都可以使用Server.GetLastError()获得最后一个异常,也可以使用 this.Context.AllErrors获得全部的未处理异常,你配置的页面当然也可以,只要他是一个aspx页面就行。

//获得最近一个Exception
Exception ex = this.Context.Server.GetLastError();
//获得所有未处理的Exception集
Exception[] errors = this.Context.AllErrors;
//然后你可以对他们进行处理了

3、网上有人推荐使用Web.config和Application_Error结合的方式来处理异常可以吗?当然可以!但个人感觉这不是个好主意,因为没啥必要。

我们看一下他们会怎么干:

Web.config

<customErrors mode="On" defaultRedirect="Error.aspx">

Global.asax

protected void Application_Error(object sender, EventArgs e)
{
//获得最后一个Exception
Exception ex = this.Context.Server.GetLastError();
//然后进行一系列处理,写文件日志,写数据库都行
//清除掉Exception,OK啦
this.Context.Server.ClearError();
}

Error.aspx

protected void Page_Load(object sender, EventArgs e)
{
//输出期望的错误页面
}

这样看起来没问题吧,其实问题大了!你会发现它根本就不往Error.aspx页面跳转!为 什么?因为Application_Error的处理优先级高于Web.config里面配置的自定义错误处理页面,你在 Application_Error里面已经把所有该做的都做了,然后还把Error给Clear掉了,此时已经不会再触发Web.config里面配置 的自动跳转了!

有人会说了,那我在Application_Error里面处理完之后不执行那个ClearError(),他不就跳转过去了吗?没错!这样一定是 可以跳转的,而且你甚至可以在Error.aspx里面把这个错误再取出来一次,然后根据错误类型给用户一个“友好的”错误提示,最后别忘了再执行 ClearError。这样是可以达到目的的,可是我真的觉得没必要,为什么没必要,看下面。

4、之所以有人采取3中的做法,无非是考虑到既想进行相应的错误捕捉处理,又想给用户一个“友好的”自定义界面。要达到这样的效果并不一定非得把两个结合起来,使用任意一种方法都能做到:

A、先看使用Web.config配置的方法:

Web.config

<customErrors mode="On" defaultRedirect="Error.aspx">

Error.aspx

protected void Page_Load(object sender, EventArgs e)
{
//获得最近一个Exception
Exception ex = this.Context.Server.GetLastError();
//然后你可以对他们进行处理了
}

B、使用Application_Error处理

Global.asax

protected void Application_Error(object sender, EventArgs e)
{
//获取Exception
Exception ex = this.Context.Server.GetLastError();
//处理Exception
//清除当前的输出
this.Context.Response.Clear();
//转向执行你希望展示给用户看的错误提示页面样子(此时网址依然是出错的那个页面,但是展示的内容就完全是你自己指定的页面了)
this.Context.Server.Transfer("/Error.aspx");

}

[转载]ASP.NET中Application_Error对Exception的集中处理

mikel阅读(1051)

[转载]ASP.NET中Application_Error对Exception的集中处理 – 随它去吧 – 博客园.

ASP.NET中对Exception的统一集中处理常见的有两种方式:

1、通过配置Web.config的customErrors节点,配置defaultRedirect属性来实现所有“未处理的异常”均跳转向同一页面

2、通过Global.asax中的Application_Error写代码实现自己想要的操作

但在实际使用当中,往往会出现一些问题,比如Application_Error中的代码未被执行,若使用customErrors那么如何传递错误信息给目标页面等等。

通过我的摸索,总结出一些经验,记录如下:

1、不想通过配置Web.config中customErrors的方法进行页面跳转,而是希望在Application_Error中写代码进行 一些处理然后用Response.Redirect方法跳转到指定页面,这种理论上很不错的方法实际上行不通!Application_Error中写 Response.Redirect来跳转页面不会执行!

2、很多人不想使用配置Web.config实现出错自动跳转的原因无非是:跳转到了一个“死”链接,无法传递当前的错误信息过去,也就没办法进行 相应的处理!其实这是一个误解,任何一个aspx网页都可以使用Server.GetLastError()获得最后一个异常,也可以使用 this.Context.AllErrors获得全部的未处理异常,你配置的页面当然也可以,只要他是一个aspx页面就行。

//获得最近一个Exception
Exception ex = this.Context.Server.GetLastError();
//获得所有未处理的Exception集
Exception[] errors = this.Context.AllErrors;
//然后你可以对他们进行处理了

3、网上有人推荐使用Web.config和Application_Error结合的方式来处理异常可以吗?当然可以!但个人感觉这不是个好主意,因为没啥必要。

我们看一下他们会怎么干:

Web.config

<customErrors mode="On" defaultRedirect="Error.aspx">

Global.asax

protected void Application_Error(object sender, EventArgs e)
{
//获得最后一个Exception
Exception ex = this.Context.Server.GetLastError();
//然后进行一系列处理,写文件日志,写数据库都行
//清除掉Exception,OK啦
this.Context.Server.ClearError();
}

Error.aspx

protected void Page_Load(object sender, EventArgs e)
{
//输出期望的错误页面
}

这样看起来没问题吧,其实问题大了!你会发现它根本就不往Error.aspx页面跳转!为 什么?因为Application_Error的处理优先级高于Web.config里面配置的自定义错误处理页面,你在 Application_Error里面已经把所有该做的都做了,然后还把Error给Clear掉了,此时已经不会再触发Web.config里面配置 的自动跳转了!

有人会说了,那我在Application_Error里面处理完之后不执行那个ClearError(),他不就跳转过去了吗?没错!这样一定是 可以跳转的,而且你甚至可以在Error.aspx里面把这个错误再取出来一次,然后根据错误类型给用户一个“友好的”错误提示,最后别忘了再执行 ClearError。这样是可以达到目的的,可是我真的觉得没必要,为什么没必要,看下面。

4、之所以有人采取3中的做法,无非是考虑到既想进行相应的错误捕捉处理,又想给用户一个“友好的”自定义界面。要达到这样的效果并不一定非得把两个结合起来,使用任意一种方法都能做到:

A、先看使用Web.config配置的方法:

Web.config

<customErrors mode="On" defaultRedirect="Error.aspx">

Error.aspx

protected void Page_Load(object sender, EventArgs e)
{
//获得最近一个Exception
Exception ex = this.Context.Server.GetLastError();
//然后你可以对他们进行处理了
}

B、使用Application_Error处理

Global.asax

protected void Application_Error(object sender, EventArgs e)
{
//获取Exception
Exception ex = this.Context.Server.GetLastError();
//处理Exception
//清除当前的输出
this.Context.Response.Clear();
//转向执行你希望展示给用户看的错误提示页面样子(此时网址依然是出错的那个页面,但是展示的内容就完全是你自己指定的页面了)
this.Context.Server.Transfer("/Error.aspx");

}

[转载]对Jquery+JSON+WebService的一点认识

mikel阅读(1274)

[转载]对Jquery+JSON+WebService的一点认识 – tyb1222 – 博客园.

JQuery作为一款优秀的JS框架,简单易用的特性就不必说了。在实际的开发过程中,使用JQ的AJAX函数调用WebService

的接口实现AJAX的功能也成了一种比较普遍的技术手段了。WebService接口的实现,通常都是由OOP语言实现的。所以

在WebService的接口函数中,难免可能会遇到除了简单数据类型的复杂数据类型。复杂的数据的数据类型机有可能是

WebService接口中的参数,也有可能是WebService的返回值。本文所叙述的要点为:

1、对于WebService接口复杂类型的参数,JQ调用的时候传入的JSON数据应该如何表示。?

2、JQ对WebService调用获取JSON数据类型。

3、JQ调用的时对Webservice返回的复杂数据类型有什么样要求。?

环境:JQ版本:1.4.2、VS2008 SP1。

测试一:对于WebService简单参数类型:

01 WebService接口函数代码如下:
02
03 [WebMethod(Description = "测试方法")]
04 public string ProcessPersonalInfo(Person person)
05 {
06 return person.Name + person.Tel;
07 }
08 JQ调用代码如下:
09
10 $.ajax({
11
12 type: "POST",
13
14 url: "WebService1.asmx/GetString",
15
16 dataType: "json",
17
18 contentType: "application/json; charset=utf-8",
19
20 data: "{'name':'zhangsan'}",
21
22 success: function(json) { alert(json.d) },
23
24 error: function(error) {
25
26 alert("调用出错" + error.responseText);
27
28 }
29 });

提示:在$.ajax函数中,data必须要以字符串的形式表示JSON,而不能直接用JSON数据传进去。可能有些朋友对JSON对象和JSON对象的字符串

不大好区分,其实,字符串类似C#里用“”引起来的东西,而JSON对象是直接写在{}中的。简单的测试方法是直接通过alert函数弹出,如果显示[object:object]

则为JSON对象,否则就是一个字符串。

结果如下图:

测试二:对于WebService复杂参数类型:

01 WebService接口函数代码如下:
02
03 [WebMethod(Description = "测试方法")]
04 public string ProcessPersonalInfo(Person person)
05 {
06 return person.Name + person.Tel;
07 }
08
09 Person实体:
10
11 public class Person
12 {
13 public string Name { get; set; }
14
15 public int Age { get; set; }
16
17 public string Address { get; set; }
18
19 public string Tel { get; set; }
20
21 }
22
23 JQ调用代码如下:
24
25 $.ajax({
26
27 type: "POST",
28
29 url: "WebService1.asmx/ProcessPersonalInfo",
30
31 dataType: "json",
32
33 contentType: "application/json; charset=utf-8",
34
35 data: "{'person':{'Name':'zhangsan','Age':28,'Address':'beijing',Tel:'01082658866'}}"
36
37 success: function(json) { alert(json.d) },
38
39 error: function(error) {
40
41 alert("调用出错" + error.responseText);
42 }
43 });

结果如下图:

调用过程与简单参数类型类似,就是通过在JS中用一个表示Person的person对象的字符串,发往客户端后,WebService会自动将person对象的字符串

转换为Person实体对象。

测试三:对于WebService复杂返回类型

01 WebService接口函数代码如下:
02
03 [WebMethod(Description = "测试方法")]
04 public List<Person> GetPersonalList()
05 {
06 List<Person> persons = new List<Person>
07 {
08 new Person {Address = "beijing", Age = 25, Name = "zhangshan", Tel = "01082678866"}
09 };
10 return persons;
11 }<br> JQ调用代码如下:
12
13 $.ajax({
14
15 type: "POST",
16
17 url: "WebService1.asmx/GetPersonalList",
18
19 dataType: "json",
20
21 contentType: "application/json; charset=utf-8",
22
23 success: function(json) { $(json.d).each(function() { alert(this.Name + "-" + this.Age + "-" + this.Address + "-" + this.Tel) }) },
24
25 error: function(error) {
26
27 alert("调用出错" + error.responseText);
28
29 }
30
31 });

如下图:

也就是说对于复杂返回类型,处理方式也是简单类型基本上是一样的。

曾听到有一种观念认为,Jq调用时WebSevice,用JSON作为数据交互格式时,返回数据类型一定是可序列化的。真的是这样吗。?

.Net的基本数据类型确实是可序列化的,这一点没有疑问。那么List<T>数据类型是否可以序列化呢。?看看List<T>的元数据(Metadata)信息

就知道了。。

[DebuggerTypeProxy(typeof (Mscorlib_CollectionDebugView<T>))]

[DebuggerDisplay(“Count = {Count}”)]

[Serializable]

public class List<T> : IList<T>, ICollection<T>, IEnumerable<T>, IList, ICollection, IEnumerable

{

/**/

}

如果上面的说法成立,在这种情况下,调用成功也无可厚非。但是问题真是这样吗。?下面继续测试一下:

测试四:对于WebService复杂返回类型

01 [WebMethod(Description = "测试方法")]
02 public Person GetPerson()
03 {
04 Person person = new Person {<br>                               Address = "beijing", Age = 27, <br>                               Name = "zhangshan", Tel = "01082678866" <br>                              };
05 return person;
06 }
07
08 JQ调用代码如下:
09
10 $.ajax({
11
12 type: "POST",
13
14 url: "WebService1.asmx/GetPerson",
15
16 dataType: "json",
17
18 contentType: "application/json; charset=utf-8",
19
20 //data: "{'person':{'Name':'zhangsan','Age':28,'Address':'beijing',Tel:'01082658866'}}",
21
22 success: function(json) { $(json.d).each(function() { alert(this.Name + "-" + this.Age + "-" + this.Address + "-" + this.Tel) }) },
23
24 error: function(error) {
25
26 alert("调用出错" + error.responseText);
27
28 }
29
30 });

如下图:

但是测试四中,GetPerson()方法返回Person数据类型。再看看Person实体的定义,根本就没有标记问可序列化。

由结果可知:JQ调用WebService,并不一定需要返回复杂类型的数据必须是可序列化的。

下面做一个有趣的测试。大家都知道WebService的返回类型不能为Hashtable类型。因为它实现了因为它实现 IDictionary接口。

测试五:对于WebService复杂返回类型

01 [WebMethod(Description = "测试方法")]
02 public Hashtable GetPersonalHashtable()
03 {
04 Hashtable hashtable = new Hashtable();
05
06 Person person = new Person { Address = "beijing", Age = 25, Name = "zhangshan", Tel = "01082678866" };
07
08 hashtable.Add(1, person);
09
10 return hashtable;
11 }
12
13 JQ调用代码如下:
14
15 $.ajax({
16
17 type: "POST",
18
19 url: "WebService1.asmx/GetPersonalHashtable",
20
21 dataType: "json",
22
23 contentType: "application/json; charset=utf-8",
24
25 data: data,
26
27 success: function(json) { $(json.d).each(function() { alert(this["one"].Name) }) },
28
29 error: function(error) {
30
31 alert("调用出错" + error.responseText);
32
33 }
34
35 });

这样,Jq居然能调用成功。这点是有点让人意想不到的。

总结:

1、Jq与WebService之间以JSON作为数据交换形式的时候,contentType: “application/json; charset=utf-8″是必须指定的。

要不然WebService不知道以何种数据作为转换。

2、Jq调用WebService返回复杂数据类型并不一定需要类型为可序列化。

3、WebService返回的JSON数据通过”.d”获取如上面测试中的alert(json.d)

[转载]使用ASP.NET MVC3+EF+Jquery制作文字直播系统(一)

mikel阅读(1003)

[转载]使用ASP.NET MVC3+EF+Jquery制作文字直播系统(一) – 田念明 – 博客园.

在人民网:http://news.people.com.cn/GB/124421/index.html ,新华网:http://www.xinhuanet.com/xhzb/index.htm网站都有文字直播。昨天刚刚完成了学校六十校庆上用的文字直播系统,当然不是使用MVC做的。今天我再使用ASP.NET MVC3+EF+JQuery完善一下这个系统,也作为Entity Framework 4 in Action读书笔记系列前期的一个例子吧。

创建解决方案和项目

1.  首先,新建一个空的解决方案

解决方案的名称为:LiveText,如下图:

QQ截图20111013115139

2.  创建完解决方案,还需要创建三个项目,具体如下面的表格:

项目名称 Visual Studio项目模板 用途
LiveText.Domain C#类库 保存域的实体和逻辑
LiveText.WebUI ASP.NET MVC 3 Web Application 存储控制器和视图
LiveText.UnitTests Test Project 单元测试

3.  添加引用

我们的项目中使用到了Ninject,Moq工具类库,首先需要添加对它们的引用,简便的方法是使用VS的Package Manager Console(View ➤ Other Windows ➤Package Manager Console),输入下面的命令:

Install-Package Ninject -Project LiveText.WebUI

Install-Package Ninject -Project LiveText.UnitTests

Install-Package Moq -Project LiveText.UnitTests

具体如下图:

QQ截图20111013120246

具体项目之间的依赖关系如下表:

项目名称 工具依赖 项目依赖
LiveText.Domain 没有 没有
LiveText.WebUI Ninject LiveText.Domain
LiveText.UnitTests Ninject,Moq LiveText.Domain,LiveText.WebUI

4.  设置依赖注入容器

项目中,我们使用Ninject创建控制器和处理依赖注入(DI)。在LiveText.WebUI项目中新建一个Infrastructure的文件夹,在该文件夹中新建一个NinjectControllerFactory类,代码如下:

public class NinjectControllerFactory : DefaultControllerFactory
{
    private IKernel ninjectKernel;
    public NinjectControllerFactory()
    {
        ninjectKernel = new StandardKernel();
        AddBindings();
    }
    protected override IController GetControllerInstance(RequestContext requestContext,
    Type controllerType)
    {
        return controllerType == null
        ? null
        : (IController)ninjectKernel.Get(controllerType);
    }
    private void AddBindings()
    {
    }
}

然后修改Global.asax如下

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();

    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);

    //修改的这个地方
    ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());
}

效果如下图:

QQ截图20111013120349

至此,项目的基本框架就做完了,下面设计数据库。

设计数据库

这里使用EF Code-First。

1.  编写实体类

人民网的文字直播系统分为“国新办发布会直播”、“国台办发布会直播”等类别,每个类别下面又有很多直播的内容。文字直播系统大体需要这几个实体类:

Category       ——      类别类                          Title      ——      标题类

Text      ——      文字类                          User      ——      用户类

在LiveText.Domain项目中新建一个文件夹Entities,在该文件夹中新建上面四个类:

QQ截图20111013150243

public class Category
{
    /// <summary>
    /// 类别编号
    /// </summary>
    public int CategoryID { get; set; }
    /// <summary>
    /// 类别名称
    /// </summary>
    public string Name { get; set; }

    /// <summary>
    /// 标题集合
    /// </summary>
    public ICollection<Title> Titles { get; set; }
}
public class Title
{
    /// <summary>
    /// 标题编号
    /// </summary>
    public int TitleID { get; set; }
    /// <summary>
    /// 标题名称
    /// </summary>
    public string Name { get; set; }

    /// <summary>
    /// 所属类别
    /// </summary>
    public Category Category { get; set; }
    /// <summary>
    /// 文字集合
    /// </summary>
    public ICollection<Text> Texts { get; set; }
}
public class Text
{
    /// <summary>
    /// 文字编号
    /// </summary>
    public int TextID { get; set; }
    /// <summary>
    /// 发言人
    /// </summary>
    public string Prolocutor { get; set; }
    /// <summary>
    /// 发言内容
    /// </summary>
    public string ProContent { get; set; }
    /// <summary>
    /// 日期
    /// </summary>
    public DateTime ProDate { get; set; }

    /// <summary>
    /// 所属标题
    /// </summary>
    public Title Title { get; set; }
}
public class User
{
    /// <summary>
    /// 用户编号
    /// </summary>
    public int UserID { get; set; }
    /// <summary>
    /// 用户名
    /// </summary>
    public string UserName { get; set; }
    /// <summary>
    /// 用户密码
    /// </summary>
    public string Password { get; set; }
}

2.  添加EFCodeFirst

在Package Manager Console中输入命令:

Install-Package EFCodeFirst -Project LiveText.Domain

QQ截图20111013152422

3.  创建上下文类

在LiveText.Domain项目中,新建名为Concrete的文件夹,在该文件夹中新建一个LiveTextDbContext的类,它继承自System.Data.Entity.DbContext,具体代码如下:

public class LiveTextDbContext : DbContext
{
    public DbSet<Category> Categories { get; set; }
    public DbSet<Title> Titles { get; set; }
    public DbSet<Text> Texts { get; set; }
    public DbSet<User> Users { get; set; }
}

4.  修改Web.config

打开LiveText.WebUI项目的Web.config,添加一个数据库连接字符串,name的值要和上下文类的名称一样。

  <connectionStrings>
    <add name="LiveTextDbContext" 
         connectionString="Data Source=.;Initial Catalog=LiveText;Integrated Security=True;Pooling=False" 
         providerName="System.Data.SqlClient"/>
  </connectionStrings>

新建一个HomeController,添加如下代码:

public class HomeController : Controller
{
    LiveTextDbContext context = new LiveTextDbContext();

    //
    // GET: /Home/

    public ActionResult Index()
    {
        var categories = context.Categories;
        return View(categories);
    }

}

给Index添加一个View,如下图:

QQ截图20111013161349

现在就可以运行了,运行结果如下:

QQ截图20111013161922

再看看数据库里,EF已经为我们自动生成了数据库,数据库的结构如下图:

QQ截图20111013162156

至此,我们数据库的设计就完成了。

源代码下载地址:http://files.cnblogs.com/nianming/LiveText201110131625.rar

[转载]如何在ASP.NET MVC中使用图表控件

mikel阅读(1178)

[转载]如何在ASP.NET MVC中使用图表控件 – caling – 博客园.

图表ASP.NET MVC的实现,是很容易的 。微软发布了一个强大的ASP.NET 的图表控制,支持丰富的图表选项设置-包括列,点,泡沫,饼图,圆环图,金字塔,漏斗,盒形图,面积,范围,AJAX的互动,以及更多 。 Microsoft图表控件示例项目包括ASP.NET页的图表样本超过200个 。在这篇文章中,我将展示如何在ASP.NET MVC中使用图表控 件。

我的示例项目是在ASP.NET MVC 2中 。
我 这里介绍一个非常简单的项目,显示了一个类的结果比较。两个字段 – ID(这是唯一的一个学生)和GPA(平均成绩) – 代表一个特定的学生的结果。 各种图表结果显示,学生的结果进行比较。我希望把重点放在如何轻松地显示相同的数据不同的结果。在这个项目中,您可以添加,编辑和删除学生的成绩,并动态 显示的变化。

要运行该项目,必须安装以下微软NET Framework 3.5的Microsoft图表控件组件。

代码开始,你将需要引用的System.Web.UI.DataVisualization程序集 。

一旦你这样做,这是相当多的简单图表添加到视图页面。

<img src=”/Chart/CreateChart?chartType=<%=System.Web.UI.DataVisualization.Charting.SeriesChartType.Column%>” alt=”” />

代码直接贴上

首先定义一个controller,提供以下方法实现

#region Chart Component

public FileResult CreateChart(SeriesChartType chartType)
{
IList peoples = _resultService.GetResults();
Chart chart = new Chart();
chart.Width = 700;
chart.Height = 300;
chart.BackColor = Color.FromArgb(211, 223, 240);
chart.BorderlineDashStyle = ChartDashStyle.Solid;
chart.BackSecondaryColor = Color.White;
chart.BackGradientStyle = GradientStyle.TopBottom;
chart.BorderlineWidth = 1;
chart.Palette = ChartColorPalette.BrightPastel;
chart.BorderlineColor = Color.FromArgb(26, 59, 105);
chart.RenderType = RenderType.BinaryStreaming;
chart.BorderSkin.SkinStyle = BorderSkinStyle.Emboss;
chart.AntiAliasing = AntiAliasingStyles.All;
chart.TextAntiAliasingQuality = TextAntiAliasingQuality.Normal;
chart.Titles.Add(CreateTitle());
chart.Legends.Add(CreateLegend());
chart.Series.Add(CreateSeries(peoples,chartType));
chart.ChartAreas.Add(CreateChartArea());

MemoryStream ms = new MemoryStream();
chart.SaveImage(ms);
return File(ms.GetBuffer(), @"image/png");
}

[NonAction]
public Title CreateTitle()
{
Title title = new Title();
title.Text = "Result Chart";
title.ShadowColor = Color.FromArgb(32, 0, 0, 0);
title.Font = new Font("Trebuchet MS", 14F, FontStyle.Bold);
title.ShadowOffset = 3;
title.ForeColor = Color.FromArgb(26, 59, 105);

return title;
}

[NonAction]
public Legend CreateLegend()
{
Legend legend = new Legend();
legend.Name = "Result Chart";
legend.Docking = Docking.Bottom;
legend.Alignment = StringAlignment.Center;
legend.BackColor = Color.Transparent;
legend.Font = new Font(new FontFamily("Trebuchet MS"), 9);
legend.LegendStyle = LegendStyle.Row;

return legend;
}

[NonAction]
public Series CreateSeries(IList results, SeriesChartType chartType)
{
Series seriesDetail = new Series();
seriesDetail.Name = "Result Chart";
seriesDetail.IsValueShownAsLabel = false;
seriesDetail.Color = Color.FromArgb(198, 99, 99);
seriesDetail.ChartType = chartType;
seriesDetail.BorderWidth = 2;
seriesDetail["DrawingStyle"] = "Cylinder";
seriesDetail["PieDrawingStyle"] = "SoftEdge";
DataPoint point;

foreach (ResultModel result in results)
{
point = new DataPoint();
point.AxisLabel =result.ID;
point.YValues = new double[] {double.Parse(result.GPA) };
seriesDetail.Points.Add(point);
}
seriesDetail.ChartArea = "Result Chart";

return seriesDetail;
}

[NonAction]
public ChartArea CreateChartArea()
{
ChartArea chartArea = new ChartArea();
chartArea.Name = "Result Chart";
chartArea.BackColor = Color.Transparent;
chartArea.AxisX.IsLabelAutoFit = false;
chartArea.AxisY.IsLabelAutoFit = false;
chartArea.AxisX.LabelStyle.Font = new Font("Verdana,Arial,Helvetica,sans-serif", 8F, FontStyle.Regular);
chartArea.AxisY.LabelStyle.Font = new Font("Verdana,Arial,Helvetica,sans-serif", 8F, FontStyle.Regular);
chartArea.AxisY.LineColor = Color.FromArgb(64, 64, 64, 64);
chartArea.AxisX.LineColor = Color.FromArgb(64, 64, 64, 64);
chartArea.AxisY.MajorGrid.LineColor = Color.FromArgb(64, 64, 64, 64);
chartArea.AxisX.MajorGrid.LineColor = Color.FromArgb(64, 64, 64, 64);
chartArea.AxisX.Interval = 1;

return chartArea;
}

#endregion

图表类的各种属性,可以控制宽度,高度,边框颜色,背景颜色,皮肤,调色板,等。最终形成图片格式展现在页面。

这里介绍的项目是ASP.NET MVC的图表控件的一个小demo示例,最终展示如下:

结果,chart.png