﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-dead-horse-随笔分类-node.js</title><link>http://www.cppblog.com/dead-horse/category/17540.html</link><description /><language>zh-cn</language><lastBuildDate>Sat, 26 Nov 2011 14:56:10 GMT</lastBuildDate><pubDate>Sat, 26 Nov 2011 14:56:10 GMT</pubDate><ttl>60</ttl><item><title>nodejs模块connect源码分析</title><link>http://www.cppblog.com/dead-horse/archive/2011/11/26/161007.html</link><dc:creator>dead_horse</dc:creator><author>dead_horse</author><pubDate>Sat, 26 Nov 2011 13:01:00 GMT</pubDate><guid>http://www.cppblog.com/dead-horse/archive/2011/11/26/161007.html</guid><wfw:comment>http://www.cppblog.com/dead-horse/comments/161007.html</wfw:comment><comments>http://www.cppblog.com/dead-horse/archive/2011/11/26/161007.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/dead-horse/comments/commentRss/161007.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/dead-horse/services/trackbacks/161007.html</trackback:ping><description><![CDATA[<div><div><span style="border-collapse: separate; font-family: Tahoma; line-height: normal; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; font-size: medium; ">connect是一个web server中间件。</span></div><div><span style="border-collapse: separate; font-family: Tahoma; line-height: normal; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; font-size: medium; ">使用方法：</span></div><div><p>&nbsp;</p><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">var</span>&nbsp;connect&nbsp;=&nbsp;require('connect');<br />connect(<br />connect.static(__dirname&nbsp;+&nbsp;'/public',&nbsp;{&nbsp;maxAge:&nbsp;0&nbsp;})<br />,&nbsp;<span style="color: #0000FF; ">function</span>(req,&nbsp;res)&nbsp;{<br />res.setHeader('Content-Type',&nbsp;'text/html');<br />res.end('&lt;img&nbsp;src="/tobi.jpeg"&nbsp;/&gt;')<br />}<br />).listen(3000);</div><p>&nbsp;</p><p><span style="border-collapse: separate; font-family: Tahoma; line-height: normal; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; font-size: medium; ">思路：</span></p><p><span style="border-collapse: separate; font-family: Tahoma; line-height: normal; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; font-size: medium; ">通过connect创建一个http|https server，提供http server的所有功能。</span></p><p><span style="border-collapse: separate; font-family: Tahoma; line-height: normal; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; font-size: medium; ">connect是原型继承于http server的，它会用use到的中间件替换掉server的requestListener。</span></p><p><span style="border-collapse: separate; font-family: Tahoma; line-height: normal; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; font-size: medium; ">通过connect.use(route, handle)来对每一个路由添加中间件，这些中间件handle会与route绑定保存在一个stack里面，每次有request请求的时候，遍历这个堆，找到对应route的handle，执行handle，如果handle最后调用了next(),就会继续寻找并执行下一个匹配的handle。</span></p><p><span style="border-collapse: separate; font-family: Tahoma; line-height: normal; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; font-size: medium; ">通过封装handle，可以很容易的在connect基础上添加更多的middleware。</span></p><p>&nbsp;</p><p><span style="border-collapse: separate; font-family: Tahoma; line-height: normal; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; font-size: medium; ">connect.js</span></p><p><span style="border-collapse: separate; font-family: Tahoma; line-height: normal; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; font-size: medium; ">有一个createServer方法，可以通过connect()访问到。根据第一个参数，如果是object，就当作是https的选项，创建HTTPSServer，如果第一个参数不是object，则创建HTTPServer，所有的参数（除了https的选项）都是一个中间件handle，会在HTTPServer绑定到&#8216;/&#8217;路径上。</span></p><p><span style="border-collapse: separate; font-family: Tahoma; line-height: normal; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; font-size: medium; ">HTTPSServer是在HTTPServer的基础上添加了一层，可以启用HTTPS服务。</span></p><p><span style="border-collapse: separate; font-family: Tahoma; line-height: normal; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; font-size: medium; ">同时，connect.js会读取middleware文件夹，把里面的中间件读取到，为他们创建getter,可以通过connect.static()访问到。</span></p><p><span style="border-collapse: separate; font-family: Tahoma; line-height: normal; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; font-size: medium; ">而每一个中间件文件暴露在外的函数都是返回一个handle。</span></p><p>&nbsp;</p><p><span style="border-collapse: separate; font-family: Tahoma; line-height: normal; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; font-size: medium; ">http.js</span></p><p><span style="border-collapse: separate; font-family: Tahoma; line-height: normal; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; font-size: medium; ">HTTPServer：初始化的时候会把所有的参数当作handle存放进stack，然后以handle方法为requestListener调用http.Server方法。</span></p><p><span style="border-collapse: separate; font-family: Tahoma; line-height: normal; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; font-size: medium; ">HTTPServer随后会继承http.Server的原型。</span></p><p><span style="border-collapse: separate; font-family: Tahoma; line-height: normal; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; font-size: medium; ">use(route, handle)</span></p><p><span style="border-collapse: separate; font-family: Tahoma; line-height: normal; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; font-size: medium; ">把handle去除外壳之后绑定到route上面，存入stack中。</span></p><p><span style="border-collapse: separate; font-family: Tahoma; line-height: normal; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; font-size: medium; ">handle(req, res, next)</span></p><p><span style="border-collapse: separate; font-family: Tahoma; line-height: normal; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; font-size: medium; ">遍历整个stack，寻找到req.url与route匹配的元素，执行它的handle。当所有的元素都遍历完还有错误，则输出。</span></p><p>&nbsp;</p><p><span style="border-collapse: separate; font-family: Tahoma; line-height: normal; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; font-size: medium; ">util.js</span></p><p><span style="border-collapse: separate; font-family: Tahoma; line-height: normal; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; font-size: medium; ">这是一个工具包，里面包含了用到的各种工具函数。</span></p><p><span style="border-collapse: separate; font-family: Tahoma; line-height: normal; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; font-size: medium; ">pause(obj)</span></p><p><span style="border-collapse: separate; font-family: Tahoma; line-height: normal; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; font-size: medium; ">把传递进来的obj对象的'data'和'end&#8216;事件都保存下来，返回两个函数：end()：不再保存事件。resume()：停止保存并把之前保存的事件释放出去给obj再次捕获，达到暂停这个obj对象的效果。（感觉可能会有bug,如果在这里释放的时候又有'data'或者'end'事件触发会不会导致顺序变乱？）</span></p><p><span style="border-collapse: separate; font-family: Tahoma; line-height: normal; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; font-size: medium; ">parseCookie(str)</span></p><p><span style="border-collapse: separate; font-family: Tahoma; line-height: normal; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; font-size: medium; ">把str以;或者，为分隔符分开。每一个都是一个cookie键值对，然后再以=分开。去除value的引号。每个键只能被取得一次。<br /><br />中间件:<br /><br />router<br /></span></p><div><div><span style="border-collapse: separate; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; ">connect的route使用方法和express类似。</span></div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008080; ">1</span>&nbsp;connect(connect.route(<span style="color: #0000FF; ">function</span>(app){<br /><span style="color: #008080; ">2</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;app.get('/:id',&nbsp;middle1,&nbsp;middle2,&nbsp;cb);<br /><span style="color: #008080; ">3</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;app.post('/admin',&nbsp;cbpost);<br /><span style="color: #008080; ">4</span>&nbsp;}));</div></div><div><span style="border-collapse: separate; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; "><br clear="none" /></span></div><div><span style="border-collapse: separate; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; ">route.js内有一个_methods数组，存放所有的route请求方法名称。（get/post/put/...）。</span></div><div><span style="border-collapse: separate; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; ">methods对象和routes对象根据_methods内的名称，包含着响应的元素，如：methods['get'], routes['get']。</span></div><div><span style="border-collapse: separate; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; "><br /></span></div><div><span style="border-collapse: separate; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; ">methods对象，根据_methods数组内的方法名称，为每一个方法调用来一个生产函数，这个函数首先把routes对象内的成员赋值[]，然后返回一个函数，这个函数用来产生routes的内容。</span></div><div><span style="border-collapse: separate; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; ">methods还有一个元素param,调用它可以为path中出现了某个param的时候设置对应的处理方法。</span></div><div><span style="border-collapse: separate; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; ">例如：</span></div><div><span style="border-collapse: separate; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; ">app.param('id', function(req, res, next, val){})，</span></div><div><span style="border-collapse: separate; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; ">当path中有param id出现的时候，会先调用这个注册的函数再进行后面的操作。</span></div><div><span style="border-collapse: separate; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; "><br /></span></div><div><span style="border-collapse: separate; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; ">在进行完上述对象的初始化之后，route模块会进行fn.call(this, methods)的调用,即用methods作为参数调用传递进来的匿名函数。所以在app.get('/:id', cb, cb1);的时候，实际调用的是methods.get('/:id', cb, cb1)，而methods.get即是之前生产函数的返回函数。</span></div><div><span style="border-collapse: separate; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; ">这个函数的处理：cb为这条路由的handle，middle1..middle2..等中间件函数将会存放在cb.middleware数组中(这里会产生一个bug)。然后把'/'转化成为正则对象，然后在转化正则的时候，可能会遇到路径里面有:id等key，会把这些key存放到keys里面。</span></div><div><span style="border-collapse: separate; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; ">最终的routes内将会多处一条routes['GET']的记录：</span></div><div><p>&nbsp;</p><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->GET:<br />[&nbsp;{&nbsp;fn:&nbsp;[Object],<br />path:&nbsp;/^\/(?:([^\/]+?))\/?$/i,<br />keys:&nbsp;[Object],<br />orig:&nbsp;'/:id',<br />method:&nbsp;'GET'&nbsp;}&nbsp;]</div><p>&nbsp;</p><div><span style="border-collapse: separate; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; ">刚才说会产生一个bug，是当有两条以上的route以cb作为handle的时候：</span></div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->app.get('/:id',&nbsp;middle1,&nbsp;middle2,&nbsp;cb);<br />app.get('/:id/test',&nbsp;middle3,&nbsp;cb);</div></div><div><span style="border-collapse: separate; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; ">因为最终的handle都是cb，此时cb的middleware数组会在第二次处理get的时候把第一次的覆盖掉，造成第一次的middleware被替换。</span></div><div><span style="border-collapse: separate; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; "><br /></span></div><div><span style="border-collapse: separate; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; ">至此，所有的准备工作完成了，然后会返回一个router函数作为handle。</span></div><div><span style="border-collapse: separate; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; "><br /></span></div><div><span style="border-collapse: separate; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; ">实际request请求触发的时候：</span></div><div><span style="border-collapse: separate; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; "><br /></span></div><div><span style="border-collapse: separate; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; ">作为handle的router函数被调用，先通过match(req, routes, i)函数，查找req.method对应方法的route的path，与req.pathName匹配。找到路径匹配的把这个route内的这个对象内的fn，同时把keys params method存放到fn里面整合称为一个route返回。返回的route内容形式为</span></div></div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->{&nbsp;[Function]<br />&nbsp;&nbsp;middleware:&nbsp;[&nbsp;[Function],&nbsp;[Function]&nbsp;],<br />&nbsp;&nbsp;keys:&nbsp;[&nbsp;'id'&nbsp;],<br />&nbsp;&nbsp;method:&nbsp;'GET',<br />&nbsp;&nbsp;params:&nbsp;[&nbsp;id:&nbsp;'ca'&nbsp;]&nbsp;}</div></div><div><span style="border-collapse: separate; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; ">然后函数去寻找是否通过methods.param定义了这条route中的param的处理函数，如果有，在这里就执行完对应param的处理函数。之后执行middleware数组内的函数，最后执行这个route。即上一段中说到的fn。这之中能够链式执行下去的条件是中间函数都执行了next()，继续调用下去，当然也可以其中某个函数就结束整个处理。<br /><br />bodyParser<br /><br /><div><div><span style="border-collapse: separate; font-family: Tahoma; line-height: normal; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; font-size: medium; ">bodyParser用来解析post方法传递过来的参数。</span></div><div><span style="border-collapse: separate; font-family: Tahoma; line-height: normal; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; font-size: medium; ">只接受mime类型为</span></div><div><span style="border-collapse: separate; font-family: Tahoma; line-height: normal; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; font-size: medium; ">application/x-www-form-urlencoded</span></div><div><span style="border-collapse: separate; font-family: Tahoma; line-height: normal; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; font-size: medium; ">application/json</span></div><div><span style="border-collapse: separate; font-family: Tahoma; line-height: normal; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; font-size: medium; ">multipart/form-data</span></div><div><span style="border-collapse: separate; font-family: Tahoma; line-height: normal; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; font-size: medium; ">三种的非GET和HEAD请求。</span></div><div><span style="border-collapse: separate; font-family: Tahoma; line-height: normal; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; font-size: medium; "><br clear="none" /></span></div><div><span style="border-collapse: separate; font-family: Tahoma; line-height: normal; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; font-size: medium; ">application/x-www-form-urlencoded通过模块qs.parse来解析。</span><div><span style="border-collapse: separate; font-family: Tahoma; line-height: normal; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; font-size: medium; ">application/json通过JSON.parse解析。</span></div><div><span style="border-collapse: separate; font-family: Tahoma; line-height: normal; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; font-size: medium; ">multipart/form-data是文件上传，通过formidable解析。<br /><br /><br />static<br /><div><div><span style="border-collapse: separate; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; ">static是一个静态文件服务器。</span></div><div><span style="border-collapse: separate; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; ">connect.static(root, options)会产生一个handle，handle设置默认的options然后调用send函数。</span></div><div><span style="border-collapse: separate; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; "><br clear="none" /></span></div><div><span style="border-collapse: separate; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; ">options内容：</span></div><div><span style="border-collapse: separate; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; ">root:静态服务器的根路径，必须由connect.static传入。</span></div><div><span style="border-collapse: separate; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; ">path:访问的文件路径</span></div><div><span style="border-collapse: separate; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; ">getOnly:访问方法限制（默认是true：只允许get方法访问&nbsp;）</span></div><div><span style="border-collapse: separate; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; ">maxAge:时间限制</span></div><div><span style="border-collapse: separate; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; ">redirect:在访问的路径是目录的时候，如果允许redirect，则会redirect到这个目录下的index.html文件，默认为true</span></div><div><span style="border-collapse: separate; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; ">callback:在每次静态服务之后调用的函数（包括发生错误，发生错误之后不会再调用next）。</span></div><div><span style="border-collapse: separate; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; ">hidden:是否允许访问隐藏文件（默认为false）</span></div><div><span style="border-collapse: separate; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; "><br clear="none" /></span></div><div><span style="border-collapse: separate; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; ">根据这些参数来决定访问限制。</span></div><div><span style="border-collapse: separate; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; "><br clear="none" /></span></div><div><span style="border-collapse: separate; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; ">支持conditional和range。</span></div><div><span style="border-collapse: separate; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; "><br clear="none" /></span></div><div><span style="border-collapse: separate; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; ">最终通过</span></div><div><span style="border-collapse: separate; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; ">var stream = fs.createReadStream(path, opts);</span></div><div><span style="border-collapse: separate; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; ">stream.pipe(res);</span></div><div><span style="border-collapse: separate; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; ">管道的方式来传送文件内容。</span></div></div></span></div></div></div></span></div></div><p>&nbsp;</p></div></div><img src ="http://www.cppblog.com/dead-horse/aggbug/161007.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/dead-horse/" target="_blank">dead_horse</a> 2011-11-26 21:01 <a href="http://www.cppblog.com/dead-horse/archive/2011/11/26/161007.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Nodejs + mongoDB 使用初体验</title><link>http://www.cppblog.com/dead-horse/archive/2011/09/23/156617.html</link><dc:creator>dead_horse</dc:creator><author>dead_horse</author><pubDate>Fri, 23 Sep 2011 07:15:00 GMT</pubDate><guid>http://www.cppblog.com/dead-horse/archive/2011/09/23/156617.html</guid><wfw:comment>http://www.cppblog.com/dead-horse/comments/156617.html</wfw:comment><comments>http://www.cppblog.com/dead-horse/archive/2011/09/23/156617.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/dead-horse/comments/commentRss/156617.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/dead-horse/services/trackbacks/156617.html</trackback:ping><description><![CDATA[<div> 	<title></title> 	 	   <pre>  最近在做nodejs的web开发，初次接触到mongoDB这个数据库。<br />  其实之前对关系型数据库的接触也不是很多，不过在刚接触使用mongoDB的时候还是习惯性的把关系型数据库的设计思维带了进去。在设计数据库的时候，还是把一些关系型数据库设计的思维带进去了，没有发挥出mongoDB文档型数据库的优势。mongoDB可以方便的把一些本来mySQL需要通过一对多关系关联的数据通过数组+对象的方式作为一个文档存储到一个collections里面，并且提供了各种API支持对一个文档里面的数组进行操作。    此次实践选用的node中间件是mongoskin，不过惭愧的是基本没有用到mongoskin的太多特性，基本当作node-mongodb-native来使用。不过写完之后，对于mongoDB的查询API以及node同mongoDB的交互有了一定的了解。<br />  <br />  MongoDB常用查询方法与在node中的体现：  <br />1）数据库连接 <br />mongoDB： mongo -u username -p password host:port/dbs <br />node+mongoSkin: require(&#8220;mongoskin&#8221;).db(username:password@host:port/dbs);  <br /> <br />2)索引 person.ensureIndex({&#8220;name&#8221;:1},{&#8220;unique&#8221;:true}, callback);<br />第一个参数是selector，第二个参数是选项，有unique（唯一索引）等mongoDB索引选项。<br />ensureIndex先查找是否存在这个索引，如果不存在，则建立索引，因此不会出现重复建立的情况。<br /><br />3）新建数据<br />person.save({&#8220;name&#8221;:&#8221;jobs&#8221;, age:32, gender:&#8221;male&#8221;}, callback);<br />person.save({&#8220;name&#8221;：&#8221;tim&#8221;, age:29, gender:&#8221;male&#8221;}, callback);<br /><br />4）查询 <br />findOne方法：查询符合selector条件的结果，取第一条给callBack，err是错误信息，如果没有错误，则为undefined，data数据为一个js对象。 <br />person.findOne({&#8220;name&#8220;:&#8221;jobs&#8221;}), callBack(err, data));  <br /><br />find方法：查询符合selector条件，并且还可以加入一系列参数，最后通过toArray方法，把数据生成数组的方式传递给callback。 <br />person.find({&#8220;gender&#8221;:&#8221;male&#8221;},{sort:[['name', 1]],skip:pageNum*(page-1), limit:pageNum}).toArray(callback(err, data){}) <br />同时查询的时候可以通过$in,$gt,$lt等方式来确定selector的范围。  <br /><br />5）更改 <br />有两点要注意：<br />1.update方法如果是要更新文档中的一个或几个属性的时候，必须要用形如{$set:{age:33}}的形式，如果写成{age:33}的形式，则这个文档的其他内容都会被删除，只剩{age:32}这一个属性。 <br />2.update方法默认是只更新找到的第一个文档，如果需要所有的文档都更新，则需要在option部分再加上{multi:true},如果想要在查找不到这条记录的时候新增一条，则要在option部分加上{upsert:true}。 <br />person.update({&#8220;name&#8221;:&#8221;jobs&#8221;}, {$set{&#8220;age&#8221;:33}}, {multi:true}, callback(err))  <br /><br />6)删除 person.remove({&#8220;name&#8221;:&#8221;jobs&#8221;},callback(err){});  <br /><br />7）selector中使用mongoDB自动生成的_id <br />mongoDB会为每一个文档生成一个_id属性，类似于mySQL的主键，是唯一的。_id的类型是mongoDB自己定义的objectID类型，因此尽管在查询的时候可以得到一个12位或者24位的_id字符串，但是如果想在selector里面通过_id进行查找或其他操作的时候，必须要先通过db.collection.id()方法进行类型转换。 <br />person.remove({&#8220;_id&#8221;:person.id(stringID)}, callback(err){});  <br /><br />8）mongoDB对文档内的数组进行操作（通过update方法） <br />1.通过$addToSet方法向数组中插入记录，插入前该方法会先查找是否存在这条记录，如果存在则不插入。如果想要插入重复的值，则可以通过$push进行添加。 <br />person.update({&#8220;name&#8221;:&#8221;jobs&#8221;}, {$addToSet: {<br /><span class="Apple-style-span" style="font-family: verdana, 'courier new'; white-space: normal; ">company: {<br /></span><span class="Apple-style-span" style="font-family: verdana, 'courier new'; white-space: normal; ">name: &#8220;google&#8221;,<br /></span><span class="Apple-style-span" style="font-family: verdana, 'courier new'; white-space: normal; ">address: USA,<br /></span><span class="Apple-style-span" style="font-family: verdana, 'courier new'; white-space: normal; ">workingTime: 3<br /></span><span class="Apple-style-span" style="font-family: verdana, 'courier new'; white-space: normal; ">}<br /></span><span class="Apple-style-span" style="font-family: verdana, 'courier new'; white-space: normal; ">}, function(err){});</span></pre> <p align="LEFT" style="margin-bottom: 0cm"><br /> </p> <p align="LEFT" style="margin-bottom: 0cm">2.修改数组中的数据：通过$符。如果在数组中查询的时候要匹配两个属性，必须要使用$elemMatch方法，如果只通过{"name":&#8221;google&#8221;, "address":USA}去查找，会选择到所有name为google或者address为USA的元素。在定位到这个元素之后，通过$来选定它进行修改。</p> <p align="LEFT" style="margin-bottom: 0cm">person.update({</p> <p align="LEFT" style="margin-bottom: 0cm">    &#8220;name&#8221;:&#8221;jobs&#8221;,</p> <p align="LEFT" style="margin-bottom: 0cm">    company:{$elemMatch:{"name":&#8221;google&#8221;, "address":USA}}</p> <p align="LEFT" style="margin-bottom: 0cm">  }, {$set:{"company.$.workingTime":4}},function(){})</p> <p align="LEFT" style="margin-bottom: 0cm"><br /> </p> <p align="LEFT" style="margin-bottom: 0cm">3.删除数组中的数据：通过$pull方法</p> <p align="LEFT" style="margin-bottom: 0cm">person.update({</p> <p align="LEFT" style="margin-bottom: 0cm">    &#8220;name&#8221;:&#8221;jobs&#8221;,</p> <p align="LEFT" style="margin-bottom: 0cm">  },{$pull:{company:{&#8220;name&#8221;:&#8221;google&#8221;, &#8220;address&#8221;:&#8221;USA&#8221;}}},function(err){})</p></div><img src ="http://www.cppblog.com/dead-horse/aggbug/156617.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/dead-horse/" target="_blank">dead_horse</a> 2011-09-23 15:15 <a href="http://www.cppblog.com/dead-horse/archive/2011/09/23/156617.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>nodeJS + mongoDB简单多表查询与js 数组去重</title><link>http://www.cppblog.com/dead-horse/archive/2011/09/14/155717.html</link><dc:creator>dead_horse</dc:creator><author>dead_horse</author><pubDate>Tue, 13 Sep 2011 17:42:00 GMT</pubDate><guid>http://www.cppblog.com/dead-horse/archive/2011/09/14/155717.html</guid><wfw:comment>http://www.cppblog.com/dead-horse/comments/155717.html</wfw:comment><comments>http://www.cppblog.com/dead-horse/archive/2011/09/14/155717.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/dead-horse/comments/commentRss/155717.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/dead-horse/services/trackbacks/155717.html</trackback:ping><description><![CDATA[&nbsp; mongoDB是不支持多表查询的，而nodeJS又是异步的，导致多表查询比较麻烦。<br />&nbsp; 一个十分简陋的多表查询方法（只有一个关联条件）：先从第一个collection中查询得到数据，将其中两个collection关联的field从中取出来并去重，通过$in在第二个collections中查询。<br />&nbsp; &nbsp;在写数组去重的时候，发现js语言特性写这种函数比C++轻松太多。<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">&nbsp; &nbsp; &nbsp;&nbsp;</span><span style="color: #0000FF; ">var</span><span style="color: #000000; ">&nbsp;users&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;[],&nbsp;uhash</span><span style="color: #000000; ">=</span><span style="color: #000000; ">{};</span><span style="color: #008000; ">//</span><span style="color: #008000; ">uhash是辅助用hash表</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">for</span><span style="color: #000000; ">(</span><span style="color: #0000FF; ">var</span><span style="color: #000000; ">&nbsp;i</span><span style="color: #000000; ">=</span><span style="color: #000000; ">0</span><span style="color: #000000; ">,&nbsp;len</span><span style="color: #000000; ">=</span><span style="color: #000000; ">data.length;&nbsp;i</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">length;&nbsp;</span><span style="color: #000000; ">++</span><span style="color: #000000; ">i){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(!uhash[data[i].email])&nbsp;{</span><span style="color: #008000; ">//</span><span style="color: #008000; ">只有hash表中不存在再插入</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;uhash[data[i].email]&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">true</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;users.push(data[i].email);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></div><br />&nbsp; hash可以非常方便的帮助完成一些辅助数组的任务。<img src ="http://www.cppblog.com/dead-horse/aggbug/155717.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/dead-horse/" target="_blank">dead_horse</a> 2011-09-14 01:42 <a href="http://www.cppblog.com/dead-horse/archive/2011/09/14/155717.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>NAE管理系统开发（nodejs+express+mongoDB）小结</title><link>http://www.cppblog.com/dead-horse/archive/2011/08/24/154190.html</link><dc:creator>dead_horse</dc:creator><author>dead_horse</author><pubDate>Tue, 23 Aug 2011 17:48:00 GMT</pubDate><guid>http://www.cppblog.com/dead-horse/archive/2011/08/24/154190.html</guid><wfw:comment>http://www.cppblog.com/dead-horse/comments/154190.html</wfw:comment><comments>http://www.cppblog.com/dead-horse/archive/2011/08/24/154190.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/dead-horse/comments/commentRss/154190.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/dead-horse/services/trackbacks/154190.html</trackback:ping><description><![CDATA[&nbsp; 这算是我第一个web的程序，写的各种丑陋不堪..记录一些不算收获的东西。看到哪写到哪。<br /><br />&nbsp; 1、路由中间件：用户登录检测和权限检测可以交给路由中间件去处理。<br /><div style="background-color: #eeeeee; font-size: 13px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-style: solid; border-right-style: solid; border-bottom-style: solid; border-left-style: solid; border-top-color: #cccccc; border-right-color: #cccccc; border-bottom-color: #cccccc; border-left-color: #cccccc; padding-right: 5px; padding-bottom: 4px; padding-left: 4px; padding-top: 4px; width: 98%; word-break: break-all; "><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; "><br />app.post(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">/application/manage/:id/coopmng</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;hasLogin,&nbsp;checkChangeAuth(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">infoRight</span><span style="color: #000000; ">"</span><span style="color: #000000; ">),&nbsp;manager.doCoopmng);</span></div>&nbsp; checkChangeAuth通过返回一个函数，所以可以在路由中间件中传递参数进去，更加灵活。<br /><br />&nbsp; 2、通过prototype，可以十分轻易的扩展内置的对象：扩展Date，添加format方法。<br /><div style="background-color: #eeeeee; font-size: 13px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-style: solid; border-right-style: solid; border-bottom-style: solid; border-left-style: solid; border-top-color: #cccccc; border-right-color: #cccccc; border-bottom-color: #cccccc; border-left-color: #cccccc; padding-right: 5px; padding-bottom: 4px; padding-left: 4px; padding-top: 4px; width: 98%; word-break: break-all; "><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008000; ">/*</span><span style="color: #008000; ">*<br />*&nbsp;时间对象的格式化;<br /></span><span style="color: #008000; ">*/</span><span style="color: #000000; "><br />Date.prototype.format&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">function</span><span style="color: #000000; ">(format){<br />&nbsp;</span><span style="color: #008000; ">/*</span><span style="color: #008000; "><br />&nbsp;&nbsp;*&nbsp;eg:format="YYYY-MM-dd&nbsp;hh:mm:ss";<br />&nbsp;&nbsp;</span><span style="color: #008000; ">*/</span><span style="color: #000000; "><br />&nbsp;</span><span style="color: #0000FF; ">var</span><span style="color: #000000; ">&nbsp;o&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;{<br />&nbsp;&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">M+</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;:&nbsp;&nbsp;</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">.getMonth()</span><span style="color: #000000; ">+</span><span style="color: #000000; ">1</span><span style="color: #000000; ">,&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">month</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">d+</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;:&nbsp;&nbsp;</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">.getDate(),&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">day</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">h+</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;:&nbsp;&nbsp;</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">.getHours(),&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">hour</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">m+</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;:&nbsp;&nbsp;</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">.getMinutes(),&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">minute</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">s+</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;:&nbsp;&nbsp;</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">.getSeconds(),&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">second</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">q+</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;:&nbsp;&nbsp;Math.floor((</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">.getMonth()</span><span style="color: #000000; ">+</span><span style="color: #000000; ">3</span><span style="color: #000000; ">)</span><span style="color: #000000; ">/</span><span style="color: #000000; ">3</span><span style="color: #000000; ">),&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">quarter</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">S</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;&nbsp;:&nbsp;&nbsp;</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">.getMilliseconds()&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">millisecond</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">(</span><span style="color: #000000; ">/</span><span style="color: #000000; ">((</span><span style="color: #000000; ">|</span><span style="color: #000000; ">Y</span><span style="color: #000000; ">|</span><span style="color: #000000; ">)</span><span style="color: #000000; ">+</span><span style="color: #000000; ">)</span><span style="color: #000000; ">/</span><span style="color: #000000; ">.test(format))&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;format&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;format.replace(RegExp.$</span><span style="color: #000000; ">1</span><span style="color: #000000; ">,&nbsp;(</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">.getFullYear()</span><span style="color: #000000; ">+</span><span style="color: #000000; ">""</span><span style="color: #000000; ">).substr(</span><span style="color: #000000; ">4</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">-</span><span style="color: #000000; ">&nbsp;RegExp.$</span><span style="color: #000000; ">1</span><span style="color: #000000; ">.length));<br />&nbsp;&nbsp;&nbsp;}<br />&nbsp;<br />&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">for</span><span style="color: #000000; ">(</span><span style="color: #0000FF; ">var</span><span style="color: #000000; ">&nbsp;k&nbsp;</span><span style="color: #0000FF; ">in</span><span style="color: #000000; ">&nbsp;o)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;RegExp(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">+</span><span style="color: #000000; ">&nbsp;k&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; ">"</span><span style="color: #000000; ">)</span><span style="color: #000000; ">"</span><span style="color: #000000; ">).test(format))&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;format&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;format.replace(RegExp.$</span><span style="color: #000000; ">1</span><span style="color: #000000; ">,&nbsp;RegExp.$</span><span style="color: #000000; ">1</span><span style="color: #000000; ">.length</span><span style="color: #000000; ">==</span><span style="color: #000000; ">1</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">?</span><span style="color: #000000; ">&nbsp;o[k]&nbsp;:&nbsp;(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">00</span><span style="color: #000000; ">"</span><span style="color: #000000; ">+</span><span style="color: #000000; ">&nbsp;o[k]).substr((</span><span style="color: #000000; ">""</span><span style="color: #000000; ">+</span><span style="color: #000000; ">&nbsp;o[k]).length));<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;}<br />&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;format;<br />}</span></div>&nbsp;&nbsp;<br />&nbsp; 3、cookie处理：在登录的时候，给它设置一个cookie，设定好过期时间，内容包含：userName,timestamp,checkCode=md5(userName, password, timestamp, skey)。skey是保存在服务器端的一个私钥。在登录验证cookie的时候，先检测时间是否过期（不能只依靠cookie的自动失效），然后再取出user现在的password，重新计算checkCode。可以保证用户修改密码后之前的cookie会失效。<br /><br />&nbsp; &nbsp;4、通过eventProxy，可以并行查询数据库，提高效率，同时让代码简洁。<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">&nbsp; &nbsp; checkEventProxy.assign(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">checkName</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">checkEmail</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #0000FF; ">function</span><span style="color: #000000; ">(goodName,&nbsp;goodEmail){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;console.log(goodName);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">(</span><span style="color: #000000; ">!</span><span style="color: #000000; ">goodName)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;res.render(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">error</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;{message:</span><span style="color: #000000; ">"</span><span style="color: #000000; ">昵称已经被注册</span><span style="color: #000000; ">"</span><span style="color: #000000; ">});<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">(</span><span style="color: #000000; ">!</span><span style="color: #000000; ">goodEmail)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;res.render(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">error</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;{message:</span><span style="color: #000000; ">"</span><span style="color: #000000; ">email已经被注册</span><span style="color: #000000; ">"</span><span style="color: #000000; ">});<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">else</span><span style="color: #000000; ">{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;users.save({email:userEmail,&nbsp;nickName:userNickName,&nbsp;password:userPassword},&nbsp;</span><span style="color: #0000FF; ">function</span><span style="color: #000000; ">(err){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">(err){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.error(err);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;res.render(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">error</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;{message:</span><span style="color: #000000; ">"</span><span style="color: #000000; ">注册失败，请稍后再试</span><span style="color: #000000; ">"</span><span style="color: #000000; ">});<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">else</span><span style="color: #000000; ">{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;req.session.email&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;userEmail;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;req.session.nickName&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;userNickName;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;res.redirect(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">/application</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;});<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">检查email是否已经存在</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;users.findOne({email:userEmail.toString()},</span><span style="color: #0000FF; ">function</span><span style="color: #000000; ">(err,&nbsp;item){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">(err){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.error(err);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;checkEventProxy.trigger(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">checkEmail</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #0000FF; ">false</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span><span style="color: #0000FF; ">else</span><span style="color: #000000; ">{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">(item)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;checkEventProxy.trigger(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">checkEmail</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #0000FF; ">false</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">else</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;checkEventProxy.trigger(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">checkEmail</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #0000FF; ">true</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;});<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">检查昵称是否已经存在</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;users.findOne({nickName:userNickName.toString()},&nbsp;</span><span style="color: #0000FF; ">function</span><span style="color: #000000; ">(err,&nbsp;item){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">(err){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.error(err);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;checkEventProxy.trigger(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">checkName</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #0000FF; ">false</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span><span style="color: #0000FF; ">else</span><span style="color: #000000; ">{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">(item)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;checkEventProxy.trigger(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">checkName</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #0000FF; ">false</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">else</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;checkEventProxy.trigger(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">checkName</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #0000FF; ">true</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;});<br />&nbsp;&nbsp;&nbsp;&nbsp;}</span></div><br />&nbsp; 5、通过谷歌smtp.gmail.com或者其他的smtp服务提供商，可以发送邮件（不需要sendmail支持）。nodemailer模块（npm install nodemailer）可以十分简单的发送邮件以及附件。<br /><br />&nbsp; 6、mongoDB不支持多表查询，所以可能要有适当的数据表项冗余。同时mongoDB不提供事务，也无法完全保证原子性。<br />&nbsp; &nbsp; &nbsp;mongoDB选定collection后的增删查改操作:<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">db.collections(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">users</span><span style="color: #000000; ">"</span><span style="color: #000000; ">).findOne({userEmail:email},{nickName:</span><span style="color: #000000; ">1</span><span style="color: #008000; ">/*</span><span style="color: #008000; ">查询结果包含nickName</span><span style="color: #008000; ">*/</span><span style="color: #000000; ">},&nbsp;</span><span style="color: #0000FF; ">function</span><span style="color: #000000; ">(err,data){});</span></div><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">db.collections(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">users</span><span style="color: #000000; ">"</span><span style="color: #000000; ">).find({userEmail:email},{skip:</span><span style="color: #000000; ">10</span><span style="color: #008000; ">/*</span><span style="color: #008000; ">跳过10条记录</span><span style="color: #008000; ">*/</span><span style="color: #000000; ">,&nbsp;limit:</span><span style="color: #000000; ">10</span><span style="color: #008000; ">/*</span><span style="color: #008000; ">结果最多10条，。实现分页功能</span><span style="color: #008000; ">*/</span><span style="color: #000000; ">}).toArray(</span><span style="color: #0000FF; ">function</span><span style="color: #000000; ">(err,data){});</span></div><br /><div style="background-color: #eeeeee; font-size: 13px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-style: solid; border-right-style: solid; border-bottom-style: solid; border-left-style: solid; border-top-color: #cccccc; border-right-color: #cccccc; border-bottom-color: #cccccc; border-left-color: #cccccc; padding-right: 5px; padding-bottom: 4px; padding-left: 4px; padding-top: 4px; width: 98%; word-break: break-all; "><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">db.collections(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">users</span><span style="color: #000000; ">"</span><span style="color: #000000; ">).save({</span><span style="color: #008000; ">/*</span><span style="color: #008000; ">save的内容</span><span style="color: #008000; ">*/</span><span style="color: #000000; ">},&nbsp;</span><span style="color: #0000FF; ">function</span><span style="color: #000000; ">(){})</span><span style="color: #008000; ">//</span><span style="color: #008000; ">插入</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">db.collections(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">users</span><span style="color: #000000; ">"</span><span style="color: #000000; ">).remove({</span><span style="color: #008000; ">/*</span><span style="color: #008000; ">条件</span><span style="color: #008000; ">*/</span><span style="color: #000000; ">},</span><span style="color: #0000FF; ">function</span><span style="color: #000000; ">(){})<br />db.collections(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">users</span><span style="color: #000000; ">"</span><span style="color: #000000; ">).update({</span><span style="color: #008000; ">/*</span><span style="color: #008000; ">条件</span><span style="color: #008000; ">*/</span><span style="color: #000000; ">},{$set:{</span><span style="color: #008000; ">/*</span><span style="color: #008000; ">内容</span><span style="color: #008000; ">*/</span><span style="color: #000000; ">}})</span></div><br />&nbsp; 7、div通过float左右分屏的时候，必须要在它们的父div添加fixClear类。通过添加一个隐藏的元素在父div最下方，让浏览器检测到两个左右浮动的div的高度，从而不会出现显示错误。<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #800000; ">.clearfix:after&nbsp;</span><span style="color: #000000; ">{</span><span style="color: #FF0000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;content</span><span style="color: #000000; ">:</span><span style="color: #0000FF; ">&nbsp;"."</span><span style="color: #000000; ">;</span><span style="color: #FF0000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;display</span><span style="color: #000000; ">:</span><span style="color: #0000FF; ">&nbsp;block</span><span style="color: #000000; ">;</span><span style="color: #FF0000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;clear</span><span style="color: #000000; ">:</span><span style="color: #0000FF; ">&nbsp;both</span><span style="color: #000000; ">;</span><span style="color: #FF0000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;visibility</span><span style="color: #000000; ">:</span><span style="color: #0000FF; ">&nbsp;hidden</span><span style="color: #000000; ">;</span><span style="color: #FF0000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;line-height</span><span style="color: #000000; ">:</span><span style="color: #0000FF; ">&nbsp;0</span><span style="color: #000000; ">;</span><span style="color: #FF0000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;height</span><span style="color: #000000; ">:</span><span style="color: #0000FF; ">&nbsp;0</span><span style="color: #000000; ">;</span><span style="color: #FF0000; "><br /></span><span style="color: #000000; ">}</span><span style="color: #800000; "><br />&nbsp;<br />.clearfix&nbsp;</span><span style="color: #000000; ">{</span><span style="color: #FF0000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;display</span><span style="color: #000000; ">:</span><span style="color: #0000FF; ">&nbsp;inline-block</span><span style="color: #000000; ">;</span><span style="color: #FF0000; "><br /></span><span style="color: #000000; ">}</span><span style="color: #800000; "><br />&nbsp;</span></div><br />&nbsp; 8.express的session，默认是存为持久的，像cookie一样要到一定时间才过期。在存session的时候，通过把req.session.cookie.expires设置成为false，可以将session设置为非持久，这样在浏览器关闭的时候，session就会消掉。<img src ="http://www.cppblog.com/dead-horse/aggbug/154190.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/dead-horse/" target="_blank">dead_horse</a> 2011-08-24 01:48 <a href="http://www.cppblog.com/dead-horse/archive/2011/08/24/154190.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>eventProxy解析</title><link>http://www.cppblog.com/dead-horse/archive/2011/08/18/153563.html</link><dc:creator>dead_horse</dc:creator><author>dead_horse</author><pubDate>Thu, 18 Aug 2011 07:24:00 GMT</pubDate><guid>http://www.cppblog.com/dead-horse/archive/2011/08/18/153563.html</guid><wfw:comment>http://www.cppblog.com/dead-horse/comments/153563.html</wfw:comment><comments>http://www.cppblog.com/dead-horse/archive/2011/08/18/153563.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/dead-horse/comments/commentRss/153563.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/dead-horse/services/trackbacks/153563.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp; &nbsp;在写node.js的时候，经常会遇到要去数据库的多个地方取得多个数据，然后才能进行下一步的操作的情况。如果是线性执行的语言，通常的做法是一条一条去取，全部取到之后再进行下一步操作。然而在node里面，因为是基于事件的，所以只能够一层一层的在回调函数里面嵌套进去。取到第一个数据之后，执行回调函数去取第二条数据，然后再执行回调函数。&nbsp; &nbsp;对于node来...&nbsp;&nbsp;<a href='http://www.cppblog.com/dead-horse/archive/2011/08/18/153563.html'>阅读全文</a><img src ="http://www.cppblog.com/dead-horse/aggbug/153563.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/dead-horse/" target="_blank">dead_horse</a> 2011-08-18 15:24 <a href="http://www.cppblog.com/dead-horse/archive/2011/08/18/153563.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>node "Can't set headers after they are sent"</title><link>http://www.cppblog.com/dead-horse/archive/2011/08/12/153110.html</link><dc:creator>dead_horse</dc:creator><author>dead_horse</author><pubDate>Thu, 11 Aug 2011 17:08:00 GMT</pubDate><guid>http://www.cppblog.com/dead-horse/archive/2011/08/12/153110.html</guid><wfw:comment>http://www.cppblog.com/dead-horse/comments/153110.html</wfw:comment><comments>http://www.cppblog.com/dead-horse/archive/2011/08/12/153110.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.cppblog.com/dead-horse/comments/commentRss/153110.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/dead-horse/services/trackbacks/153110.html</trackback:ping><description><![CDATA[&nbsp; &nbsp;最近刚开始接触web开发，学习node.js，在写的时候经常会出现Can't set headers after they are sent这个错误。<br />&nbsp; &nbsp;发现是在redirect或者render之后，node并不会跳出代码段，中断下面的执行，而是继续往下执行，当再次redirect或者render的时候，就会出现这个错误。<br /><br />&nbsp; &nbsp;要在redirect和render之前适时加上return，结束它们之后的代码执行，可以避免这个错误。<img src ="http://www.cppblog.com/dead-horse/aggbug/153110.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/dead-horse/" target="_blank">dead_horse</a> 2011-08-12 01:08 <a href="http://www.cppblog.com/dead-horse/archive/2011/08/12/153110.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>