dnn 里边,一个模块的render 事件中怎么能瘦脸获取全部页面的html代码

通过 nodeclub 项目源码来讲解如何做一个 nodejs + express + mongodb 项目 - CNode
这家伙很懒,什么个性签名都没有留下。
发布于 10个月前
5150 次浏览
通过 nodeclub 项目源码来讲解如何做一个 nodejs + express + mongodb 项目
##1. About
nodeclub 是 cnodejs.org 的源码,CNode 算是一个基本的博客系统,包含文章发布, 关注,评论等功能。这些功能可以说是任何一个网站的基础。从 nodeclub 里可以学到什么?
1.基本的架构
2.开发测试过程
3.MVC 的设计
4.middleware 的正确用法
5.如何设计 Mongodb schema
6.如何正确的使用 Mongoose
7.如何实现一个标签系统
8.plugins? services ?
9.如何正确的使用 EJS helper
10.到底该怎样写路由, restful?
11.如何做基本的控制验证
12.如何发邮件
13.session
14.GitHub 用户登录
15.图片上传
16.消息发送
除了 nodeclub 源码的学习笔记以外, 还会有一点最近捣鼓这一块的经验分享
1.一个完整的消息订阅设计
2.消息推送, socket + express 如何合作?
3.包装 action
4.蛋疼的异步回调如何处理
对于想用 nodejs
+ express + mongodb 来做网站技术基础的项目, nodeclub 可以说是很好的源码级指南,当然也是我的指南,这篇文章权当做个人学习 nodeclub 的学习笔记。
who = 一名本应该在写前端的但不知怎的一直在写后端的马脓 -&
[@echo](/user/echo) 'github: /6174'
[@echo](/user/echo) 'weibo: /u/'
[@echo](/user/echo) 'email: [@qq](/user/qq).com'
[@echo](/user/echo) 'ps: 一直在求后端partner中,有意者联系我'
[@send](/user/send)()
2. nodeclub 中用到了哪些开源技术
2.1 Node.js 项目一大优点就是有一个 package.json,
里边的 dependencies & devDependencies 可以看到这个项目所有的依赖。 对于有经验的开发者来说, 看完 package.json 基本就能知道项目的架构是怎样。
2.2 dependencies
express: 基础框架:
mongodb: 数据存储
mongoose: orm
connect-mongo: session (对于redis, 可以使用connect-redis)
nodemailer:邮件
validator:验证
passport,passport-github: passport,
loader: ejs-view-helper, 静态资源加载处理
其他: event-proxy, node-markdown, ndir
2.3 devDependencies
测试框架:mocha, should
运行: forever
请求模拟: supertest
2.4 nodeclub 以 express + mongodb + mongoose 作为基本框架, 典型的 MVC 应用
Model: 对应mongoose orm, models目录
view: ejs模板, views目录
controler:express middleware , contollers目录
2.5 目录结构:
- controllers/
# express中间件, 基本的auth, session 验证
- middlewares/
#消息, 邮件服务
- services/
- plugins/
#可以看做是对model处理的加工库
- route.js
- config.js
3. 应用入口 app.js
神圣的入口文件,几乎每个项目都会有一个 entry,对于了解一个应用熟悉入口逻辑很重要。 下面将分步来看看,nodeclub 的 app.js 做了什么:
3.1 require(./config)
3.1.1 应用相关的配置的设置, 主要分为
1.应用全局数据配置
2.数据库连接配置
3.session,auth 相关配置
5.mail配置
6.第三方连接相关配置, github, weibo
配置文件也是了解应用的一个好地方, 在 config.default.js 中可以看到以下信息, 这些很可能是我们平时做应用开发的时候没有留意到的地方
//--应用数据统计
google_tracker_id: 'UA-',
//--静态文件很可能使用cdn来做
site_static_host: '', // 静态文件存储域名
//--求解释
site_enable_search_preview: false, // 开启google search preview
site_google_search_domain:
'cnodejs.org',
// google search preview中要搜索的域名
//--运营数据
list_topic_count: 20,
post_interval: 10000,
admins: { admin: true },
side_ads:[]
allow_sign_up: true,
//--插件模式
plugins: []
3.1.2 当然这里的配置文件是 default 的,配置文件可以放在一个 config 的文件夹下面,多个文件的方式来整理。比如运营数据配置和其他数据配置分开,因为很有可能需要做一个小的工具来让非技术人员配置相关参数。这时候可以用一个 index.js 作为 facade,相当于一个大的 node module。
3.2 require('./models')
3.2.1 之前已经讲了 models/ 目录对应 MVC 的 M 部分。
3.2.2 models/ 目录下面有 index.js, require('./models') 相当于 require('./models/index')
index 相当于一个模型的 facade, index.js 做得事情分别是
1.connect mongodb
2.require 各个 model 模块
3.exports 所有的 model
简单而言就是初始化了应用 model 层。
3.2.3 模型使用 orm 框架 mogoose 来写,了解 mogoose 过后, models 部分的代码也就是秒懂了
, 我说的只是代码,literaly, 一个项目的核心就是 model 的设计,以前做过的任何项目都是一样, 数据库 table 的设计好坏直接影响应用的开发以及性能。 下面来看看各个 model 的 schema 设计(几乎直接 ctr+c, ctr+v 加上了一点点注释) :
3.2.4 user
var UserSchema = new Schema({
//--基本用户信息, index表示在mongodb中会建立索引
//--unique: true 唯一性设置
name: { type: String, index: true },
loginname: { type: String, unique: true },
pass: { type: String },
email: { type: String, unique: true },
url: { type: String },
profile_image_url: {type: String},
location: { type: String },
signature: { type: String },
profile: { type: String },
weibo: { type: String },
avatar: { type: String },
githubId: { type: String, index: true },
githubUsername: {type: String},
is_block: {type: Boolean, default: false},
//--用户产生数据meta
score: { type: Number, default: 0 },
topic_count: { type: Number, default: 0 },
reply_count: { type: Number, default: 0 },
follower_count: { type: Number, default: 0 },
following_count: { type: Number, default: 0 },
collect_tag_count: { type: Number, default: 0 },
collect_topic_count: { type: Number, default: 0 },
create_at: { type: Date, default: Date.now },
update_at: { type: Date, default: Date.now },
is_star: { type: Boolean },
level: { type: String },
active: { type: Boolean, default: true },
receive_reply_mail: {type: Boolean, default: false },
receive_at_mail: { type: Boolean, default: false },
from_wp: { type: Boolean },
retrieve_time : {type: Number},
retrieve_key : {type: String}
3.2.5 topic 话题
//tag &- topic &- collect
var TopicSchema = new Schema({
title: { type: String },
content: { type: String },
author_id: { type: ObjectId },
top: { type: Boolean, default: false },
reply_count: { type: Number, default: 0 },
visit_count: { type: Number, default: 0 },
collect_count: { type: Number, default: 0 },
create_at: { type: Date, default: Date.now },
update_at: { type: Date, default: Date.now },
//--这里reply的设计方式不知道是否合适, 因为mongdb不同于关系型数据库,这里每次读取文章都需要重reply集合里边查找遍历一边,文章是读繁忙的。
//-- 一个document的大小为5Mb, 一本牛津词典的内容, 我觉得将reply放在这里应该不会有太大问题。 即便不存放reply 内容, 存放一个id数组也会好很多。
//-- 客官们怎么看?
last_reply: { type: ObjectId },
last_reply_at: { type: Date, default: Date.now },
content_is_html: { type: Boolean }
var ReplySchema = new Schema({
content: { type: String },
topic_id: { type: ObjectId, index: true },
author_id: { type: ObjectId },
reply_id : { type: ObjectId },
create_at: { type: Date, default: Date.now },
update_at: { type: Date, default: Date.now },
content_is_html: { type: Boolean }
//--话题集合
var TopicCollectSchema = new Schema({
user_id: { type: ObjectId },
topic_id: { type: ObjectId },
create_at: { type: Date, default: Date.now }
//--话题标签
var TopicTagSchema = new Schema({
topic_id: { type: ObjectId },
tag_id: { type: ObjectId },
create_at: { type: Date, default: Date.now }
//tag &- collect
var TagSchema = new Schema({
name: { type: String },
order: { type: Number, default: 1 },
description: { type: String },
background: { type: String },
topic_count: { type: Number, default: 0 },
collect_count: { type: Number, default: 0 },
create_at: { type: Date, default: Date.now }
var TagCollectSchema = new Schema({
user_id: { type: ObjectId, index: true },
tag_id: { type: ObjectId },
create_at: { type: Date, default: Date.now }
3.2.7 关系
var RelationSchema = new Schema({
user_id: { type: ObjectId },
follow_id: { type: ObjectId },
create_at: { type: Date, default: Date.now }
3.2.8 消息
消息 model 设计, 对于一个 blog 来说, 基本的只有回复消息, 这里加了关注和@消息。
* reply: xx 回复了你的话题
* reply2: xx 在话题中回复了你
* follow: xx 关注了你
* at: xx @了你
var MessageSchema = new Schema({
type: { type: String },
master_id: { type: ObjectId, index: true },
author_id: { type: ObjectId },
topic_id: { type: ObjectId },
reply_id: { type: ObjectId },
has_read: { type: Boolean, default: false },
create_at: { type: Date, default: Date.now }
###3.3 require middlewares
3.3.1 express 的基础是 middleware,或者说 express 的基础是 connect,connect 的基础是 middleware。middleware 模式在 professional nodejs 中有一个专门的章节来讲解。何为 middleware 呢? middleware 模式 相当于一个加工流水线(大家叫 middleware stack),每一个 middleware 相当于一个加工步骤,当出现一个 http 请求的时候,http 请求会挨着每个 middleware 执行下去。
express 里处理一个请求的过程基本上就是请求通过 middleware stack 的过程:
* -& middlewares -& 路由 -& controllers -& errorhandlering。
3.3.2 middleware 怎样做到的, 异步的方法呢? middleware 使用 promise 的方式来处理异步,所有每个 middleware 都有三个参数 req, res, next, 对于异步的情况, 必须要调用 next() 方法。不然后续的 middleware 就无法执行。 ps: debug 的时候没调用 next() 还不会报错,一定注意
3.3.3 auth.js
auth.js exports 出来的函数全部都是中间件,从变量名就完全清楚的知道到底在做什么了
//-- 需要admin权限
exports.adminRequired = function (req, res, next) {}
//-- 需要有用户
exports.userRequired = function (req, res, next) {}
//-- 需要有用户并登录
exports.signinRequired = function (req, res, next) {
if (!req.session.user) {
res.render('notify/notify', {error: '未登入用户不能发布话题。'});
//-- 屏蔽用户 -_-
exports.blockUser = function (req, res, next) {}
这里其实就可以看到中间件的作用了,我们以前写 php 的时候每次都需要判断用户是否登录, 没登陆 redirect 到 index.php ,只不过这里的方式是通过中间件来处理。
明白这里什么意思,其他的中间件模块也就秒懂了。
###3.4 require(‘./routes’)
3.4.1 express 的世界里另外一个很重要的就是route, Node.js 启动的是服务, 监听了某一端口, 接受 http or https or socket 请求,
那 url 中像 /index.php?blabla 这一串的存在怎么处理呢, express 的 route 功能就可以帮我们解析。
3.4.2 MVC 中如何将一个请求和 controller 联系起来呢, route 就是这样的纽带
//--get, post 请求
app.get('/signin', sign.showLogin);
app.post('/signin', sign.login);
//--使用中间件
app.get('/signup', configMiddleware.github, passport.authenticate('github'));
app.post('/:topic_id/reply', auth.userRequired, limit.postInterval, reply.add);
3.4.3 route 是了解一个应用最佳的地方,一个请求如何处理, 到相应的 controller 去看就知道了。 相比起在PHP环境下配置更加灵活。当然你说你通过nginx来配置也很灵活,好吧,我们说的不是一回事。
3.5 initialization
3.5.1 experess initialize: app.js 中其他大多部分就是express的初始化了, 初始化流程如下:
1.配置上传 upload_dir
2.模板引擎设置
3.express 通用中间件设置
4.pasport 中间件
5.自定义中间件
1.auth_user
2.block_user
3.staticfile: upload
4.staticfile: user_data
7.errorhandler
8.set view cache
[@Note](/user/Note):配置的顺序很重要, 中间件的执行顺序是按照定义顺序来执行的, 如果一个中间件依赖另外的中间件, 而自己先执行了, 这种情况就会错误。 常见的问题就是session配置, 一定要记得配置 session 中间件的时候, 要先配置 cookieParser。
3.5.2 session 设置
这个步骤在 initialize 里边已经有了, 不过再单独讲一下, nodeclub 使用的是 connect-mongo 来作为 session 的存储
//--cookieParser一定要在前面, 因为session的设置依赖cookie
app.use(express.cookieParser());
app.use(express.session({
secret: config.session_secret,
store: new MongoStore({
db: config.db_name,
3.5.3 view helpers
使用过 ejs 的肯定知道, ejs 里边 view helper 设置很简单, 就像赋值变量一样。 当对于一些通用的 helper 可以这样设置:
app.helpers({
config: config,
Loader: Loader,
assets: assets
app.dynamicHelpers(require('./common/render_helpers'));
3.5.4 github pasport initialize
// github oauth
passport.serializeUser(function (user, done) {
done(null, user);
passport.deserializeUser(function (user, done) {
done(null, user);
passport.use(new GitHubStrategy(config.GITHUB_OAUTH, githubStrategyMiddleware));
3.5.5 start app
##4. 用户注册
4.1 user 是每个应用都会处理的基本, 注册登录登出, 看看 nodeclub 做了哪些事情:
4.2 路由:
//--设置能否直接注册, 不能的话通过github注册
if (config.allow_sign_up) {
app.get('/signup', sign.showSignup);
app.post('/signup', sign.signup);
app.get('/signup', configMiddleware.github, passport.authenticate('github'));
app.post('/signout', sign.signout);
app.get('/signin', sign.showLogin);
app.post('/signin', sign.login);
4.3 controller & model:sign.signup
sanitize = validator.
check = validator.
exports.signup = function (req, res, next) {
//--xss 消毒
var name = sanitize(req.body.name).trim();
name = sanitize(name).xss();
//--validations
check(name, '用户名只能使用0-9,a-z,A-Z。').isAlphanumeric();
} catch (e) {
res.render('sign/signup', {error: e.message, name: name, email: email});
//--用用户名登录或者email登录
query = {'$or': [{'loginname': loginname}, {'email': email}]}
User.getUserByQuery(query, {}, function(){
pass = md5(pass);
User.newAndSave(name, loginname, pass, email, avatar_url, false, function (err) {
// 发送激活邮件
mail.sendActiveMail(email, md5(email + config.session_secret), name);
res.render('sign/signup', {
success: '欢迎加入 ' + config.name + '!我们已给您的注册邮箱发送了一封邮件,请点击里面的链接来激活您的帐号。'
##5. mongoose 的使用
5.1 使用User.newAndSave,
5.2 异步 callback pyramid
一个应用通常会遇到这样的情景, 一个页面需要的数据包括, 文章列表, 评论列表,用户数据,广告数据, other stuff…
问题是每个都是异步的, 怎么办。 user 数据获取过后的 callback 调用文章列表获取, 文章列表获取的 callback 调用评论列表的获取… 这样就太蛋疼了。
nodeclub 使用了 eventproxy 模块优雅的解决这样的问题:
render = function(){}
var proxy = EventProxy.create('tags', 'topics', 'hot_topics', 'stars', 'tops', 'no_reply_topics', 'pages', render);
proxy.fail(next);
Tag.getAllTags(proxy.done('tags'));
Topic.getTopicsByQuery(query, options, proxy.done('topics'));
User.getUsersByQuery({ is_star: true }, { limit: 5 }, proxy.done('stars'));
看完代码不言而喻。。。
当然异步处理的方法有很多:
1.基于事件的:eventProxy
2.基于promise的:Async.js Q.js, when.js
3.基于编译的:continuation, wind
4.基于语言语法的:yield, livescript
文章最后会讲一下我我的异步选择方案
6.1 原先以为有动态的消息推送, 有队列处理, 错了, 木有
6.2 在 Sublime text 里边全局搜索 sendReply2Message 会发现是在 controller/reply.js 里边调用的, 也就是说,消息是直接触发的。
6.3 好吧, 这部分大概大家都能秒懂。。
###7.1 测试
7.1.1 一个项目必定离不开测试, nodeclub基于mocha BDD测试框架, 一切的前提假设至少能看懂jasmine或者mocha或者任何一个BDD风格的测试代码。
打开即看到app.js
var app = require('../app');
describe('app.js', function () {
//--before, 执行it的前面会执行
before(function (done) {
//--done, 异步方法
app.listen(3001, done);
after(function () {
app.close();
it('should / status 200', function (done) {
//--使用 app.request()就可以模拟请求了? 这个api哪里来的, 求解释?
app.request().get('/').end(function (res) {
res.should.status(200);
//--按理说应该是可以正常运行了但是我一直出现这个错误:
//--connect ADDRNOTAVAIL 知道的求解释
//--我尝试用supertest直接测试, 但是也是一直timeout, mocha
//--里边加大timeout时间, 结果就是一直没反应。
//--分析原因, express版本问题, nodeclub中express的版本还是2.x, 所以才会有
//--app.request(), app.close()这些api
//--第二个原因, 到supertest官网, 发现人家都已经转战到superagent项目了, 于是我写了下面这个测试脚本, 可以通过了
var express = require('express');
var should = require('should');
var path = require('path');
var superagent = require('superagent');
var app = express()
app.get('/user', function(req, res, next) {
res.send(200, {
name: 'tobi'
describe('myapp.js', function() {
this.timeout(5000)
before(function(done) {
app.listen(21, done);
after(function() {
// app.close()
it('should /status 200', function(done) {
agent = superagent.agent()
agent.get('http://localhost:21/user').end(function(err, res) {
console.log(err, res)
res.should.have.status(200);
res.text.should.include('tobi');
return done();
###7.2 运行
nodejs是单线程应用, 如果我们用node命令来运行我们的应用, 当出现一个小错误, 它就挂了。 然后没有然后了。
避免这种问题的方法有如下工具:
3.supervisor
nodeclub 使用 forever 来运行项目, 使用这类工具的好处就是, 当有代码改动过后, 会自动的重启应用。 不必每次自己去运行 node *.js
##8. 说说自己的经验
###8.1 消息订阅设计
###8.2 express + socket
###8.3 异步
###8.4 Action
文章不错… 就是 Markdown 格式改了我好久…
good! 已置顶。坐等更新。
nodeclub 这么烂的代码都有人愿意来看,着实让人感动不已。
关于 mongoose schema 那里楼主的疑问,解答是:nodeclub 几乎没有利用 mongodb 的特性。
nodeclub 的测试很不全,不过里面的测试大致上是最佳实践的样子。
火钳留名~支持下~
前排留名哈~
居然置顶了, 万分感动啊。
看你们代码说明是真爱啊, 哈哈!
恩, 测试部分很值得参考借鉴。
多谢支持 : -)
感谢,对自己很有帮助 :-)
收藏,慢慢看。
nodeclub 代码说实话一般啊,坑也很多
源码看过几次了
但一直没有像你一样写下来和总结心得,值得学习!
求几个好的项目源码
nodeclub也是我看的第一个node的项目,不过当时就没作者(学家?)看的这么系统了,学习了!
额,最近沉迷nodejs,求教
本来我也想写一个学习文档的。
Node Club还是比较好的学习资料,认真的看代码,并且在基础上做一定的二次开发会比写书籍上面的小代码和写个blog锻炼人。
我前一个月用Node Club搭建了一个小区的社交平台
个人感觉是看朴灵的《深入浅出Node.js》 然后用Node Club练手会是比较好的node学习方式。
几点感受:
1.node club 好歹也算的上是完整的项目,比写个blog和聊天室要锻炼的人。可以看到node的文件目录建立,和MVC设计,这边可以结合《深入浅出Node.js》中的第8章 构建Web应用。总之node club可以被当做node学习从0到1的跨越。
2.express 2.x的版本比较老,比较蛋痛。社区可以考虑更新一下。
3.和 是好东西。可以让网站的结构和mvc设计上一个档次。
4.Node Club中原来的tag标签功能和图片上传功能是被封死的,读者要在源代码上做一定的修改。
5.Node Club涉及到的异步操作不多,这部分内容大部分都被eventProxy代劳,只有在图片上传文件移动中有涉及。
6.实际发布中要用nginx方向链接,这边要去掉端口,在config中设置debug: false,邮件发送要在原来的基础上加上 secureConnection: true。
7.在cnode中搜索Node Club可以解决你在开发中遇到的问题。
8.测试这一块的内容看不懂,明天在对照书和楼主的笔记去好好看看。
很赞, 最近也是因为在做一个node项目, 太没有经验, 所有才想看一下一些现有的代码。
github上node-express-mogoose-demo那个项目其实也很赞, 在cnode上还看到了doubanj 以及 jsgen这两个项目也有很多学习借鉴的地方。
打开你的网站 , 设计很赞啊。
哈哈,除了动态栏消息全是div, 截取字符串没有去掉标签。
小泉也来混了,哈哈!多看些项目 / packages的source code,自己也写写。。
希望可以给他贡献点力量啊
忘持续更新
看了评论,结合最近经验,感觉学习项目确实是一种很好的方式,我要坚持下来!加油,希望LZ持续更新!
翻了好多页终于翻出来了……前阵子置顶的时候没收藏真是重大失误
坐等更新啊
这里有个缺点,就是好贴沉了就看不见了…感觉前端乱炖蛮好的
新手建议顺道看看 ,随心编写简单,可维护的node.js
部署运行好了,发现无法向
精华 模块写文章,求救。。
服务器搭建在
,存储赞助商为DNN皮肤白皮书中文版_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
评价文档:
13页免费21页免费6页免费3页¥2.0019页免费14页免费13页免费9页免费5页免费6页免费
喜欢此文档的还喜欢6页免费14页1下载券55页1下载券13页免费43页1下载券
DNN皮肤白皮书中文版|D​N​N​皮​肤​白​皮​书​中​文​版
把文档贴到Blog、BBS或个人站等:
普通尺寸(450*500pix)
较大尺寸(630*500pix)
你可能喜欢第一资料 地图信息这里介绍的是DNN安装包里面自带的,属于DNN核心团队免费提供的开放源码的功能模块。
Announcements
可用于发布站点的新闻、通知。支持搜索、Rss、导入、导出,支持模板功能。
用来做博客服务的程序,每一个注册用户都可以创建自己的博客。
管理联系人姓名 角色 Email 手机 固定电话
简单的文件下载管理模块,管理员可以上传文件,给简要说明。
用来记录已经或者计划要做的事情。
问题与答案的列表。
一个简单的留言板,但是具有将留言发送给特定邮件的功能。
一个功能齐全的论坛模块。
可以管理图片、flash、wma的模块,可以很好的与论坛模块结合。
帮助系统,在线技术支持模块。
和html中的IFrame的作用一样,可以很方便的将需要的页面放进来。
进行链接管理的模块
可在网站的页面上放置一个图片,可以链接一个媒体文件。
将Newsfeed的新闻显示出来。
Repository
可以用于下载、文章、blog、 图片商业名片等 管理
全功能网上商店
文本/超文本
独立的一篇文字
UserDefinedTable
用户自定义表格
用户根据自己的需要创建内容丰富的表格。
Users Online
可以显示注册用户总数,新注册用户,今天新增总数,昨天新增总数,当前在线者列表等。
将xml文件中的数据用特定的格式(给出xsl文件)展示出来。
欢迎多提宝贵意见!谢谢!
阅读(...) 评论()
Powered By:}

我要回帖

更多关于 怎么能瘦脸 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信