使用node.js的nodejs expresss模块怎么实现权限管理

例子:实现最新版本Node.js中Express+mongodb的登录注册页面
由于版本差异巨大且不兼容的情况下,作为才开始学习Node.js的菜鸟,书籍上的例子是不能看了,因此仿照着网络大神中的例子自己再归纳总结了一遍,方便自己以后查看。好记性不如烂笔头嘛。
这里主要使用的版本是express4.0+mongodb最新版本以及Bootstrap3.0界面所做。
一、新建项目文件且搭建依赖关系
下图步骤1:首先用命令行进入一个自定的项目存放地址
下图步骤2:运行命令行搭建express框架的项目文件。(我的项目文件夹取名为LoginRegister)
下图步骤3:根据提示要求命令行进入新建的项目文件夹LoginRegister
下图步骤4:运行命令创建依赖包关系
二、项目初始化成功,运行命令后打开浏览器http://localhost:3000即可查看是否成功显示Express的欢迎界面。
npm start三、题外话:文件初始情况介绍。
现在打开该目录即可看到刚才新建的LoginRegister文件夹下面新建了一些东西
bin是项目的启动文件,默认npm start启动
public是项目静态文件,放置Js、Css、Img等文件的地方
routes是项目路由信息文件,控制地址的
view是视图文件,放置ejs或jade等的也就是相当于html模板的
package.json是管理依赖包的,一般项目主要采用了哪些包都会在这里显示
四、题内话:(大概了解下即可)项目框架设计
这里还提供了注销功能,但无界面路径为/logout。
如果浏览器中直接输入http://localhost:3000/home企图直接跳入主界面,那么会有一个判断,判断是否登录成功,未登录者不允许进入。
五、为了方便使用,我们把View文件夹下面所有.ejs模板全部更改为.html后缀名。并且在app.js里面也相应修改一下配置。
六、由于上面项目框架设计知道需要几个界面模板,因此现在开始在view下面创建界面模板文件。
(1)index.html是登录注册的主界面。a标签中用到了路由路径的方法
&!DOCTYPE html&
&title&&%= title %&&/title&
&link rel='stylesheet' href='stylesheets/style.css' /&
&h1&&%= title %&&/h1&
&p&Welcome to &%= title %&&/p&
&p&&a href=&/login&&登录&/a&&/p&
&p&&a href=&/register&&注册&/a&&/p&
&/html&(2)新建register.html,它是注册界面。
首先给form表单返回return false防止默认提交。然后只有在输入正确的情况下才能通过ajax把表单信息post到路径/register里。提示:界面皆由bootstrap3.0构成。记得提前引入bootstrap的css、js以及jq。应该将js和css文件相应存入public文件夹下面哦。并且把bootstrap的fonts文件夹一起放入public根目录方便调用里面的图片。
&!DOCTYPE html&
&html lang=&en&&
&meta charset=&UTF-8&&
&meta name=&viewport& content=&width=device-width,initial-scale=1.0&&
&title&&%= title%&&/title&
&link rel=&stylesheet& href=&stylesheets/bootstrap3.3.7.min.css&&
&script type=&text/javascript& src=&javascripts/jquery1.11.3.min.js&&&/script&
&script type=&text/javascript& src=&javascripts/bootstrap3.3.7.min.js&&&/script&
.tc{text-align:font-size: 18font-weight: 600;}
.m15{margin: 15}
$(function(){
$(&#login1&).click(function (){
location.href = 'login';
$(&#register1&).click(function(){
var username = $(&#username&).val();
var password = $(&#password&).val();
var passwordAgin = $(&#passwordAgin&).val();
if(password !== passwordAgin){
$(&#password&).css(&boder&,&1px solid red&);
$(&#passwordAgin&).css(&boder&,&1px solid red&);
}else if(password === passwordAgin){
var data = {&uname&:username,&upwd&:password};
url:'/register',
type:'post',
data:data,
success:function(data,status){
if(status == 'success'){
location.href = 'login';
error:function(data,err) {
location.href = 'register';
&div class=&container&&
&form class=&form-horizontal col-sm-4 col-sm-offset-4& role=&form& method=&post& onsubmit=&return false&&
&fieldset&
&legend&&/legend&
&div class=&panel panel-default&&
&div class=&page-header&&
&p class=&tc&&注册信息&/p&
&div class=&panel-body m15&&
&div class=&form-group&&
&div class=&input-group&&
&span class=&input-group-addon&&
&span class=&glyphicon glyphicon-user&&&/span&
&input type=&text& class=&form-control& id=&username& name=&username& placeholder=&请输入用户名& required&
&div class=&form-group&&
&div class=&input-group&&
&span class=&input-group-addon&&
&span class=&glyphicon glyphicon-lock&&&/span&
&input type=&password& class=&form-control& id=&password& name=&password& placeholder=&请输入密码& required&
&div class=&form-group&&
&div class=&input-group&&
&span class=&input-group-addon&&
&span class=&glyphicon glyphicon-lock&&&/span&
&input type=&text& class=&form-control& id=&passwordAgin& name=&passwordAgin& placeholder=&请再次输入密码&&
&div class=&form-group&&
&button type=&submit& class=&btn bg-primary btn-block& id=&register1&&注册&/button&
&div class=&form-group&&
&button type=&button& class=&btn btn-info col-sm-2 col-sm-offset-10& id=&login1&&登录&/button&
&/fieldset&
&/html&(3)新建一个login.html跟上面register.html原理相似。
&!DOCTYPE html&
&html lang=&en&&
&meta charset=&UTF-8&&
&meta name=&viewport& content=&width=device-width,initial-scale=1.0&&
&title&&%= title%&&/title&
&link rel=&stylesheet& href=&stylesheets/bootstrap3.3.7.min.css&&
&script type=&text/javascript& src=&javascripts/jquery1.11.3.min.js&&&/script&
&script type=&text/javascript& src=&javascripts/bootstrap3.3.7.min.js&&&/script&
.tc{text-align:font-size: 18font-weight: 600;}
.m15{margin: 15}
$(function(){
$(&#register0&).click(function(){
location.href = 'register';
$(&#login0&).click(function(){
var username = $(&#username&).val();
var password = $(&#password&).val();
var data = {&uname&:username,&upwd&:password};
url:'/login',
type:'post',
data:data,
success:function(data,status){
if(status === 'success'){
location.href = 'home';
error: function (data,status) {
if(status === 'error'){
location.href = 'login';
&div class=&container&&
&form class=&form-horizontal col-sm-4 col-sm-offset-4& role=&form& method=&post& onsubmit=&return false&&
&fieldset&
&legend&&/legend&
&div class=&panel panel-default&&
&div class=&page-header&&
&p class=&tc&&请先登录&/p&
&div class=&panel-body m15&&
&div class=&form-group&&
&div class=&input-group&&
&span class=&input-group-addon&&
&span class=&glyphicon glyphicon-user&&&/span&
&input type=&text& class=&form-control& id=&username& name=&username& placeholder=&请输入用户名& required&
&div class=&form-group&&
&div class=&input-group&&
&span class=&input-group-addon&&
&span class=&glyphicon glyphicon-lock&&&/span&
&input type=&password& class=&form-control& id=&password& name=&password& placeholder=&请输入密码& required&
&div class=&form-group&&
&button type=&submit& class=&btn bg-primary btn-block& id=&register0&&注册&/button&
&div class=&form-group&&
&button type=&button& class=&btn btn-info col-sm-2 col-sm-offset-10& id=&login0&&登录&/button&
&/fieldset&
&/html&(4)新建home.html。
里面的user.name就是还有ejs模板通过session.user来获取的user对象。(这里user有name和password属性)
&!DOCTYPE html&
&html lang=&en&&
&meta charset=&UTF-8&&
&title&&%= title %&&/title&
&link rel=&stylesheet& href=&stylesheets/style.css&&
a{margin-left: 20text-decoration:}
a:hover{text-decoration:}
&h1&your name:&%- user.name%&&/h1&
&p&Welcom to your home&/p&
&p&&a href=&/logout&&我要注销&/a&&/p&
&/html&七、接下来需要给app.js增加路由配置,让游览器访问到路径后可以解析出来
//设置路径
app.use('/',index);
app.use('/users',users);
app.use('/login',index);
app.use('/register',index);
app.use('/home',index);
app.use('/logout',index);八、因为要实现界面之间的跳转所以需要数据库的支持,这里采用mongodb,运用到mongoose工具。
(1)在根目录创建一个database文件夹,建立文件models.js,然后建立model处理文件dbHandel.js
(2)然后在model.js里面写入user集合,包括name和password属性
module.exports = {
name:{type:String,required:true},
password:{type:String,required:true}
};(3)在dbHandel.js里面主要是获取Schema,然后处理获取model,最后就是返回一个model
var mongoose = require('mongoose');
var Schema = mongoose.S
var models = require(&./model&);
for(var m in models){
mongoose.model(m,new Schema(models[m]));
module.exports = {
getModel:function (type) {
return _getModel(type);
var _getModel = function (type) {
return mongoose.model(type);
};(4)建立好基本文件后,我们需要在app.js中调用使用它,这需要使用到multer和mongoose模块,由于项目没有,所以我们需要安装。
使用命令行进入此项目目录后运用命令安装multer和mongoose模块
(5)然后在app.js中加上multer和mongoose
//新增multer和mongoose
var multer = require('multer');
var mongoose = require('mongoose');
global.dbHandel = require('./database/dbHandel');
global.db = mongoose.connect(&mongodb://localhost:27017/nodedb&);
//新增上multer和mongoose
app.use(bodyParser.urlencoded({extended:true}));
app.use(multer());
app.use(cookieParser());
(6)因为我们用到了数据库,因此新建一个nodedb的数据库,以便存储数据。
a.首先用MongoDB.bat快捷打开mongodb数据库(具体怎么建立MongoDB.bat参照我的上文http://blog.csdn.net/miss_ll/article/details/)。使用浏览器打开http://localhost:27017/查看即可。
b.命令行进入存放mongodb的bin目录,然后初始化nodedb
(7)又因为我们用到了session(就是进入到home的时候判断session值是否为空),因此需要用到express-session模块
同样命令行进入到该项目目录中,运行命令安装express-session模块
(8)然后在app.js中引用express-session并作初始值设置。
//引用express-session
var session = require('express-session');
var app = express();
app.use (session({
secret:'secret',
app.use(function (req,res,next) {
res.locals.user = req.session.//从session获取user对象
var err = req.session.//获取错误信息
delete req.session.
res.locals.message = &&;//展示信息的message
res.locals.message = '&div class=&alert alert-danger& style=&margin-bottom:20color:red&&'+err+'&/div&';
next();//中间件传递
});九、处理路径部分。修改routes目录下的index.js。新增上几个页面对应路径。
/* GET index page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });// 到达此路径则渲染index文件,并传出title值供index.html使用
/* GET login page. */
router.route(&/login&).get(function(req,res){
// 到达此路径则渲染login文件,并传出title值供 login.html使用
res.render(&login&,{title:'User Login'});
}).post(function(req,res){
// 从此路径检测到post方式则进行post数据的处理操作
//get User info
//这里的User就是从model中获取user对象,通过global.dbHandel全局方法(这个方法在app.js中已经实现)
var User = global.dbHandel.getModel('user');
var uname = req.body.
//获取post上来的 data数据中 uname的值
User.findOne({name:uname},function(err,doc){
//通过此model以用户名的条件 查询数据库中的匹配信息
//错误就返回给原post处(login.html) 状态码为500的错误
res.send(500);
console.log(err);
}else if(!doc){
//查询不到用户名匹配信息,则用户名不存在
req.session.error = '用户名不存在';
res.send(404);
状态码返回404
res.redirect(&/login&);
if(req.body.upwd != doc.password){
//查询到匹配用户名的信息,但相应的password属性不匹配
req.session.error = &密码错误&;
res.send(404);
res.redirect(&/login&);
//信息匹配成功,则将此对象(匹配到的user) 赋给session.user
并返回成功
req.session.user =
res.send(200);
res.redirect(&/home&);
/* GET register page. */
router.route(&/register&).get(function(req,res){
// 到达此路径则渲染register文件,并传出title值供 register.html使用
res.render(&register&,{title:'User register'});
}).post(function(req,res){
//这里的User就是从model中获取user对象,通过global.dbHandel全局方法(这个方法在app.js中已经实现)
var User = global.dbHandel.getModel('user');
var uname = req.body.
var upwd = req.body.
User.findOne({name: uname},function(err,doc){
// 同理 /login 路径的处理方式
res.send(500);
req.session.error =
'网络异常错误!';
console.log(err);
}else if(doc){
req.session.error = '用户名已存在!';
res.send(500);
User.create({
// 创建一组user对象置入model
name: uname,
password: upwd
},function(err,doc){
if (err) {
res.send(500);
console.log(err);
req.session.error = '用户名创建成功!';
res.send(200);
/* GET home page. */
router.get(&/home&,function(req,res){
if(!req.session.user){
//到达/home路径首先判断是否已经登录
req.session.error = &请先登录&
res.redirect(&/login&);
//未登录则重定向到 /login 路径
res.render(&home&,{title:'Home'});
//已登录则渲染home页面
/* GET logout page. */
router.get(&/logout&,function(req,res){
// 到达 /logout 路径则登出, session中user,error对象置空,并重定向到根路径
req.session.user =
req.session.error =
res.redirect(&/&);
});十、至此项目已经成功完成了。运行命令行进入该项目目录,启动项目。
运行成功后打开浏览器输入http://localhost:3000访问界面,还可以边注册边查看mongodb数据库node的user里面数据变动哦
源码:http://download.csdn.net/detail/miss_ll/9713274
感谢,/imwtr/p/4360341.html是参照他的实现的哦~谢谢
网页截图:
看过本文的人也看了:
我要留言技术领域:
取消收藏确定要取消收藏吗?
删除图谱提示你保存在该图谱下的知识内容也会被删除,建议你先将内容移到其他图谱中。你确定要删除知识图谱及其内容吗?
删除节点提示无法删除该知识节点,因该节点下仍保存有相关知识内容!
删除节点提示你确定要删除该知识节点吗?必须谨记!Node.js安全开发技巧
发表于 16:43|
来源CODESHIP|
作者Gergely Nemeth
摘要:毫无疑问,Node.js已经变的愈加成熟,尽管这样,开发者仍然缺乏大量的安全指南。本文将分享一些有关Node.js安全要点给大家,希望大家能够谨记于心。
【编者按】互联网安全事件层出不穷,用户每时每刻都有可能会遭受攻击者袭击。作为软件开发人员,则是最大限度的保证应用程序安全。本文主要是对Node.js语言作出的安全开发建议。
以下为译文:
毫无疑问,Node.js已经变的愈加成熟,尽管这样,开发者仍然缺乏大量的安全指南。在这篇文章中,我将分享一些有关Node.js安全要点给大家,希望大家能够谨记于心。
1.避免使用Eval
Eval并不是唯一一个需要避免的函数,在后台,下面这几个表达式可以使用eval:
setInterval(String, 2)
setTimeout(String, 2)
new Function(String)
为什么要禁止使用eval?因为它会打开代码引起注入攻击,并且降低运行速度。
2.请用严苛模式(Strict mode)
这种情况下,你可以使用一个受限制的JavaScript变量,它可以消除一些隐性错误并且会把这些错误抛出去。
3.Undeletable属性
'use strict';
delete Object. // TypeError
4.对象声明必须是独一无二的
'use strict';
var obj = {
// syntax error5.Prohibits with
var obj = { x: 17 };
with (obj) // !!! syntax error
}你可以访问&来获得这些隐性错误的完整列表。
不用说,测试,测试,多点测试~
当然,不仅仅是单元测试,直戳。
7.对sudo node app.js说再见
我看到很多人在运行拥有超级用户权限的Node APP,为什么?因为他们想让应用程序能够监听80或443端口。
这种做法是错误的。小心一个错误/bug进程就能降低整个系统,因为它已经拥有合法的凭证去做任何事情。
取而代之,你应该设置一个HTTP服务器/代理来转换请求,可以是nginx、Apache等等。
8.避免命令注入
看看下面这段代码有什么问题?
child_process.exec('ls', function (err, data) {
console.log(data);
child_process.exec调用去执行/bin/sh,所以,这是一个bash注射器,而不是一个程序启动器。
当用户输入的内容传递到这个方法中,就会产生问题——要么是一个\要么就是$(),攻击者很可能会注入一个新的命令。
开发人员可以使用child_process.execFile来解决此类问题。访问
来查看如何处理命令注入。
9.临时文件
开发人员在创建文件时要特别注意,例如处理上传文件。这些文件很容易吃掉你所有的磁盘空间。
解决方法是使用Streams。
10.确保Web应用程序安全
这里不仅仅是针对Node——而是关于如何确保你的Web应用程序安全。
11.跨站脚本反射
当攻击者把代码注入到HTTP响应时,这种情况才会发生。应用程序在返回无效的输入到客户端时(大多数都是基于JavaScript编写的),应用程序很容易遭受这种类型攻击。攻击者可以盗取cookies、执行剪贴板和修改页面本身。
具体示例:
&a href="/index.php?user=&script"&/index.php?user=&script&/a&&alert(123)&/script&如果用户查询字符串在没有验证的情况下被发送回客户端,并且插入到DOM中,它便会执行。
如何预防:
禁止插入不可信的数据到DOM中;
在插入前进行HTML escape。
点击链接可以查看更多关于
12.停止Cookie盗窃
默认情况下,在同一个域里,JavaScript可以读取cookies,在跨站脚本攻击里,这是非常危险的。不仅如此,任何第三方JavaScript库都可以读取它们。
var cookies = document.cookie.split('; ');
如何预防:
开发者可以在cookies里面设置httponly标记,这样,JavaScript便无法接近cookie了。13.内容安全策略(CSP)
CSP(Content Security Policy)是一个额外添加的安全层,它有助于检测和减轻某些类型的攻击,包括跨站脚本(XSS)和数据注入攻击。
通过HTTP header中的Content-Security-Policy方法来启动CSP。
Content-Security-Policy: default-src 'self' *.这样便会允许内容来自可信域或其子域。
点击查看更多有关
14.跨站请求伪造(CSRF)
CSRF(Cross-Site Request Forgery)是一种挟制终端用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。
&body onload="document.forms[0].submit()"&
&form method="POST" action="/user/delete"&
&input type="hidden" name="id" value="123555."&
执行上面这段代码的后果就是轻易删除用户配置文件。
如何预防:
要想阻止CSRF,你应该实现同步令牌模式(Synchronizer Token Pattern),幸运地是,Node社区已经帮你完成了这件事。长话短说,一起来看看它的工作原理:
当一个GET请求被服务检查为CSRF令牌——如果不存在,就创建一个;
当用户输入显示时,确保要添加一个隐形的CSRF令牌值;
当发送表单时,确保值是来自表单并且要与session相匹配。
开发者应该有所行动,制作一个
车间来指导现实应用开发。
15.保护Express应用程序:Helmet
Helmet是一系列帮助增强Node之Express/Connect等Javascript Web应用安全的中间件。安全功能包括:
crossdomain
有关Helmet更多内容,可以访问其托管地址:
16.使用工具
npm shrinkwrap:该命令可以给一个包的所有依赖关系上锁,并且创建一个npm-shrinkwrap.json文件。更多详情可以访问
retire.js:retire.js是一个命令行扫描器,帮助你找到你应用中依赖库存在的漏洞。
如果你想了解更多关于Node安全方面的内容,你可以关注
项目。他们的目标是审核NPM中的每一个模块,发现问题并修复问题。(张红月/编译)
英文来自:
推荐阅读相关主题:
CSDN官方微信
扫描二维码,向CSDN吐槽
微信号:CSDNnews
相关热门文章使用 Node.js、Express、AngularJS 和 MongoDB
构建一个实时问卷调查应用程序
最近,在向大学生们介绍 HTML5 的时候,我想要对他们进行问卷调查,并向他们显示实时更新的投票结果。鉴于此目的,我决定快速构建一个用于此目的的问卷调查应用程序。我想要一个简单的架构,不需要太多不同的语言和框架。因此,我决定对所有一切都使用 JavaScript — 对服务器端使用 Node.js 和 Express,对数据库使用 MongoDB,对前端用户界面使用 AngularJS。这个 MEAN 堆栈(Mongo、Express、Angular 和 Node)只需要一天即可完成,远比 Web 应用程序开发和部署所用的 LAMP
堆栈(Linux、Apache、MySQL 和 PHP)简单得多。我选择使用
来管理我的项目的源代码。它不仅为我的代码提供了一个完整的版本控制系统,还为在云中编辑代码提供了一个在线 IDE,以及用于项目管理的敏捷特性。JazzHub 很容易与 Eclipse 相集成,Eclipse 还提供了一些插件,支持对平台( 比如
或 )的一键式部署。构建该应用程序的先决条件基本了解
和 Node.js 开发环境具有以下这些 Node.js 模块:、、 和
JavaScript 框架 NoSQL 数据库,已安装了
插件第 1 步. 构建一个基础 Express 后台在 Eclipse 中,切换到 Node 透视图,并创建一个新的 Node Express 项目。如果您创建了一个 JazzHub 项目,请像我所做的那样,使用相同的名称为您的 Node Express 项目命名。选择使用 Jade 作为模板引擎。Eclipse 会自动下载所需的 npm 模块,以便创建一个简单 Express 应用程序。运行这个 Express 应用程序在 Project Explorer 中,找到位于您项目的根目录中的 app.js,右键单击并选择 Run As & Node Application。这将启动一个 Web 服务器并将应用程序部署到该服务器。
接下来,打开浏览器并导航到 。图 1. Starter Express 应用程序配置基础前端这个问卷调查应用程序对常见用户界面和布局使用了 Bootstrap 框架。现在,让我们对 Express 应用程序做一些改动来反映这一点。首先,打开 routes/index.js,将标题属性更改为 Polls:清单 1. routes/index.js
exports.index = function(req, res){
res.render('index', { title: 'Polls' });
};接着,更改 views/index.jade 模板以包含 Bootstrap。Jade 是一种速记模板语言,可编译成 HTML。它使用缩进消除了对结束标签的需求,极大地缩小了模板的大小。您只需要使用 Jade 作为主页面布局即可。在下一步中,还可以使用 Angular 局部模板向这个页面添加功能。清单 2. views/index.jade
html(lang='en')
meta(charset='utf-8')
meta(name='viewport', content='width=device-width,
initial-scale=1, user-scalable=no')
title= title
link(rel='stylesheet', href='///bootstrap/3.0.1/
css/bootstrap.min.css')
link(rel='stylesheet', href='/stylesheets/style.css')
nav.navbar.navbar-inverse.navbar-fixed-top(role='navigation')
div.navbar-header
a.navbar-brand(href='#/polls')= title
div.container
div 想要查看对您的应用程序所做的更改,请结束 Eclipse 中的 Web 服务器进程,再次运行 app.js 文件:图 2. 问卷调查应用程序样板文件注意:在使用 Jade 模板时,注意适当缩进您的代码,否则您会遇到麻烦。另外,还要避免使用混合缩进样式,如果您尝试这样做,Jade 将会报错。第 2 步. 使用 AngularJS
提供前端用户体验如果要使用 Angular,首先需要在您的 HTML 页面中包含它,还需要在 HTML 页面中添加一些指令。在 views/index.jade 模板中,对 html 元素进行如下更改:html(lang='en', ng-app='polls')。 在该文件的标头中添加以下脚本元素:
: 清单 3. 将脚本元素加载到 Angular 和 Angular Resource 模板
script(src='///ajax/libs/angularjs/1.0.8/angular.min.js')
script(src='///ajax/libs/angularjs/1.0.8
/angular-resource.min.js')接下来,更改模板中的 body 元素,添加一个 ng-controller 属性(稍后使用该属性将用户界面绑定到控制器逻辑代码中):body(ng-controller='PollListCtrl')。最后,更改模板中的最后一个 div 元素,以便包含一个 ng-view 属性: div(ng-view)。构建 Angular 模块在默认情况下,Express 发布了静态资源,比如 JavaScript 源文件、CSS 样式表以及位于您项目的公共目录中的图像。在公共目录中,创建一个名为 javascripts 的新的子目录。在这个子目录中,创建一个名为 app.js 的文件。该文件将包含用于应用程序的 Angular 模块,而且还定义了用于用户界面的路由和模板:清单 4. public/javascripts/app.js
angular.module('polls', [])
.config(['$routeProvider', function($routeProvider) {
$routeProvider.
when('/polls', { templateUrl: 'partials/list.html', controller:
PollListCtrl }).
when('/poll/:pollId', { templateUrl: 'partials/item.html', controller:
PollItemCtrl }).
when('/new', { templateUrl: 'partials/new.html', controller:
PollNewCtrl }).
otherwise({ redirectTo: '/polls' });
}]);Angular 控制器定义了应用程序的范围,为要绑定的视图提供数据和方法。清单 5. public/javascript/controllers.js
// Managing the poll list
function PollListCtrl($scope) {
$scope.polls = [];
// Voting / viewing poll results
function PollItemCtrl($scope, $routeParams) {
$scope.poll = {};
$scope.vote = function() {};
// Creating a new poll
function PollNewCtrl($scope) {
$scope.poll = {
question: '',
choices: [{ text: '' }, { text: '' }, { text: '' }]
$scope.addChoice = function() {
$scope.poll.choices.push({ text: '' });
$scope.createPoll = function() {};
}创建局部 HTML 和模板为了呈现来自控制器的数据,Angular 使用了局部 HTML 模板,该模板允许您使用占位符和表达式来包含数据和执行操作,比如条件和迭代操作。在公共目录中,创建一个名为 partials 的新的子目录。我们将为我们的应用程序创建 3 个局部模板,第一个局部模板将会展示可用投票的列表,我们将使用 Angular 通过一个搜索字段轻松过滤该列表。清单 6. public/partials/list.html
&div class="page-header"&
&h1&Poll List&/h1&
&div class="row"&
&div class="col-xs-5"&
&a href="#/new" class="btn btn-default"&&span class="glyphicon
glyphicon-plus"&&/span& New Poll&/a&
&div class="col-xs-7"&
&input type="text" class="form-control" ng-model="query"
placeholder="Search for a poll"&
&div class="row"&&div class="col-xs-12"&
&hr&&/div&&/div&
&div class="row" ng-switch on="polls.length"&
&ul ng-switch-when="0"&
&li&&em&No polls in database. Would you like to
&a href="#/new"&create one&/a&?&/li&
&ul ng-switch-default&
&li ng-repeat="poll in polls | filter:query"&
&a href="#/poll/{{poll._id}}"&{{poll.question}}&/a&
&p&&&/p&第二个局部模板允许用户查看投票。它使用 Angular 切换指令来确定用户是否已投票,并根据这些判断,显示一个就此次问卷调查进行投票的表格,或者一个包含显示问卷调查结果的图表。清单 7. public/partials/item.html
&div class="page-header"&
&h1&View Poll&/h1&
&div class="well well-lg"&
&strong&Question&/strong&&br&{{poll.question}}
&div ng-hide="poll.userVoted"&
&p class="lead"&Please select one of the following options.&/p&
&form role="form" ng-submit="vote()"&
&div ng-repeat="choice in poll.choices" class="radio"&
&input type="radio" name="choice" ng-model="poll.userVote"
value="{{choice._id}}"&
{{choice.text}}
&p&&hr&&/p&
&div class="row"&
&div class="col-xs-6"&
&a href="#/polls" class="btn btn-default" role="button"&&span
class="glyphicon glyphicon-arrow-left"&&/span& Back to Poll
&div class="col-xs-6"&
&button class="btn btn-primary pull-right" type="submit"&
Vote &&/button&
&div ng-show="poll.userVoted"&
&table class="result-table"&
&tr ng-repeat="choice in poll.choices"&
&td&{{choice.text}}&/td&
&table style="width: {{choice.votes.length
/poll.totalVotes*100}}%;"&
&tr&&td&{{choice.votes.length}}&/td&&/tr&
&p&&em&{{poll.totalVotes}} votes counted so far. &span
ng-show="poll.userChoice"&You voted for &strong&{{poll.userChoice.text}}
&/strong&.&/span&&/em&&/p&
&p&&hr&&/p&
&p&&a href="#/polls" class="btn btn-default" role="button"&
&span class="glyphicon glyphicon-arrow-left"&&/span& Back to
Poll List&/a&&/p&
&p&&&/p&第三个也是最后一个局部模板定义了支持用户创建新的问卷调查的表单。它要求用户输入一个问题和三个选项。提供一个按钮,以便允许用户添加额外的选项。稍后,我们将验证用户至少输入了两个选项 — 因为如果没有几个选项,就不能称之为问卷调查。清单 8. public/partials/new.html
&div class="page-header"&
&h1&Create New Poll&/h1&
&form role="form" ng-submit="createPoll()"&
&div class="form-group"&
&label for="pollQuestion"&Question&/label&
&input type="text" ng-model="poll.question" class="form-control"
id="pollQuestion" placeholder="Enter poll question"&
&div class="form-group"&
&label&Choices&/label&
&div ng-repeat="choice in poll.choices"&
&input type="text" ng-model="choice.text" class="form-control"
placeholder="Enter choice {{$index+1}} text"&&br&
&div class="row"&
&div class="col-xs-12"&
&button type="button" class="btn btn-default" ng-click=
"addChoice()"&&span class="glyphicon glyphicon-plus"&
&/span& Add another&/button&
&p&&hr&&/p&
&div class="row"&
&div class="col-xs-6"&
&a href="#/polls" class="btn btn-default" role="button"&
&span class="glyphicon glyphicon-arrow-left"&&/span&
Back to Poll List&/a&
&div class="col-xs-6"&
&button class="btn btn-primary pull-right" type="submit"&
Create Poll &&/button&
&/form&最后,为了显示结果,我们需要向 style.css 添加一些 CSS 声明。将该文件的内容替换为以下内容:清单 9. public/stylesheets/style.css
body { padding-top: 50 }
.result-table {
margin: 20px 0;
width: 100%;
border-collapse:
.result-table td { padding: 8 }
.result-table & tbody & tr & td:first-child {
width: 25%;
max-width: 300
text-align:
.result-table td table {
background-color:
text-align:
} 此时,如果您运行该应用程序,就会看到一个空的问卷调查列表。如果您试着创建一个新的问卷调查,就能看到此表单并添加更多的选项,但您不能保存该问卷调查。我们将在下一步中详细介绍所有这些内容。第
步. 使用 Mongoose 在 MongoDB 中存储数据为了存储数据,该应用程序使用了 MongoDB
驱动程序和 Mongoose npm 模块。它们允许应用程序与 MongoDB 数据库进行通信。要获得这些模块,请打该应用程序根目录中的 package.json 文件,并在依赖关系部分中添加以下这些代码行:。清单 10. 向依赖关系添加下列代码
"mongodb": "&= 1.3.19",
"mongoose": "&= 3.8.0",保存文件,在 Project Explorer 中右键单击并选择 Run As & npm install。这将安装 npm 模块和其他所有依赖关系。 创建一个 Mongoose 模型在您应用程序的名为 models 的根目录中创建一个新的子目录,并在这个子目录中创建一个名为 Poll.js 的新文件。在这个文件中,我们定义了我们的 Mongoose 模型,该模型将用于查询数据,并以结构化数据的形式将这些数据保存到 MongoDB 。清单 11. models/Poll.js
var mongoose = require('mongoose');
var voteSchema = new mongoose.Schema({ ip: 'String' });
var choiceSchema = new mongoose.Schema({
text: String,
votes: [voteSchema]
exports.PollSchema = new mongoose.Schema({
question: { type: String, required: true },
choices: [choiceSchema]
});定义数据存储的 API 路由接下来,在您应用程序的根目录下的 app.js 文件中设置一些路由,以便创建一些 JSON 端点,这些端点可用于根据 Angular 客户端代码来查询和更新 MongoDB。找到 app.get('/', routes.index) 行,并将下列代码添加到这一行的后面:
:清单 12. 创建 JSON 端点
app.get('/polls/polls', routes.list);
app.get('/polls/:id', routes.poll);
app.post('/polls', routes.create);现在,您需要实现这些功能。将 routes/index.js 文件的内容替换为下列代码:
清单 13. routes/index.js
var mongoose = require('mongoose');
var db = mongoose.createConnection('localhost', 'pollsapp');
var PollSchema = require('../models/Poll.js').PollS
var Poll = db.model('polls', PollSchema);
exports.index = function(req, res) {
res.render('index', {title: 'Polls'});
// JSON API for list of polls
exports.list = function(req, res) {
Poll.find({}, 'question', function(error, polls) {
res.json(polls);
// JSON API for getting a single poll
exports.poll = function(req, res) {
var pollId = req.params.
Poll.findById(pollId, '', { lean: true }, function(err, poll) {
if(poll) {
var userVoted = false,
userChoice,
totalVotes = 0;
for(c in poll.choices) {
var choice = poll.choices[c];
for(v in choice.votes) {
var vote = choice.votes[v];
totalVotes++;
if(vote.ip === (req.header('x-forwarded-for') || req.ip)) {
userVoted =
userChoice = { _id: choice._id, text: choice.text };
poll.userVoted = userV
poll.userChoice = userC
poll.totalVotes = totalV
res.json(poll);
res.json({error:true});
// JSON API for creating a new poll
exports.create = function(req, res) {
var reqBody = req.body,
choices = reqBody.choices.filter(function(v) { return v.text != ''; }),
pollObj = {question: reqBody.question, choices: choices};
var poll = new Poll(pollObj);
poll.save(function(err, doc) {
if(err || !doc) {
throw 'Error';
res.json(doc);
};使用 Angular 服务将数据绑定到前端此时,设置后台,以便启用查询,并将问卷调查数据保存到数据库,但我们需要在 Angular 中做一些更改,以便让它知道如何与数据库进行通信。使用 Angular 服务很容易完成这项任务,它将与服务器端进行通信的过程包装到了简单的函数调用中:
清单 14. public/javascripts/services.js
angular.module('pollServices', ['ngResource']).
factory('Poll', function($resource) {
return $resource('polls/:pollId', {}, {
query: { method: 'GET', params: { pollId: 'polls' }, isArray: true }
});在创建的这一文件之后,您需要将它包括在您的 index.jade 模板中。在文件标头部分的最后一个脚本元素的下面添加以下这行代码:script(src='/javascripts/services.js')。您还需要告诉您的 Angular 应用程序使用这个服务模块。要实现这一点,请打开 public/javascripts/app.js 并将第一行更改为可读,如下所示: angular.module('polls', ['pollServices'])。最后,更改 Angular 控制器,以便使用该服务在数据库中进行查询和存储问卷调查数据。在 public/javascripts/controllers.js 文件中,将 PollListCtrl 更改如下:。清单 15. public/javascripts/controller.js
function PollListCtrl($scope, Poll) {
$scope.polls = Poll.query();
...更新 PollItemCtrl 函数,以便根据问卷调查的 ID 来查询某个问卷调查: 清单 16. public/javascripts/controller.js (continued)...
function PollItemCtrl($scope, $routeParams, Poll) {
$scope.poll = Poll.get({pollId: $routeParams.pollId});
$scope.vote = function() {};
...类似地,更改 PollNewCtrl 函数,以便在提交表单时将新调查数据发送到服务器。清单 17. public/javascripts/controller.js (continued)...
function PollNewCtrl($scope, $location, Poll) {
$scope.poll = {
question: '',
choices: [ { text: '' }, { text: '' }, { text: '' }]
$scope.addChoice = function() {
$scope.poll.choices.push({ text: '' });
$scope.createPoll = function() {
var poll = $scope.
if(poll.question.length & 0) {
var choiceCount = 0;
for(var i = 0, ln = poll.choices. i & i++) {
var choice = poll.choices[i];
if(choice.text.length & 0) {
choiceCount++
if(choiceCount & 1) {
var newPoll = new Poll(poll);
newPoll.$save(function(p, resp) {
if(!p.error) {
$location.path('polls');
alert('Could not create poll');
alert('You must enter at least two choices');
alert('You must enter a question');
}运行应用程序您已经离成功不远了!此时,应用程序应该允许用户查看和搜索问卷调查数据、创建新的问卷调查并查看单个问卷调查的投票选项。在运行该应用程序之前,请确保您已经本地运行 MongoDB。这通常和打开一个终端或命令提示符以及运行 mongod 命令一样简单。确保在您运行应用程序时终端窗口处于打开状态:图 3. 查看一个问卷调查的选项在运行应用程序之后,在您的浏览器中导航到 http://localhost:3000 并创建一些问卷调查。如果您单击一个问卷调查,您就能够看到可用的选项,但是,您无法实际对该问卷调查进行投票,或者暂时看不到问卷调查结果。我们将在下一步和最后一步中对此进行介绍。第 4 步. 使用 Socket.io 进行实时投票剩下的惟一需要构建的特性就是投票功能。该应用程序允许用户进行投票,在他们投票后,会在所有已连接的客户端上实时更新投票结果。使用 socket.io 模块很容易完成这项工作,现在就让我们来实现它吧。打开您应用程序的根目录中的 package.json 文件,将下列代码添加到依赖关系部分:"socket.io": "~0.9.16"。保存文件,在 Package Explorer 中右键单击,然后选择 Run As
& npm install 来安装 npm 模块。接下来,打开应用程序根目录中的 app.js 文件, 删除位于文件末尾的 server.listen... 代码块,将其替换为:清单 18. app.js...
var server = http.createServer(app);
var io = require('socket.io').listen(server);
io.sockets.on('connection', routes.vote);
server.listen(app.get('port'), function(){
console.log('Express server listening on port ' + app.get('port'));
});接下来,修改 index.jade 模块以包含 socket.io 客户端库。在运行该应用程序时,该库会自动在指定的位置上变得可用,因此不需要担心自己如何寻找该文件。确保想包含模板中的 angular-resource 库的行的后面包含此文件:script(src='/socket.io/socket.io.js')。最后,您需要创建投票功能,以便在用户向 socket.io 发送消息时保存新的投票,并在具有更新结果时将消息发送给所有客户端。将
添加到路由目录中的 index.js 文件的结尾处:清单 19. routes/index.js
// Socket API for saving a vote
exports.vote = function(socket) {
socket.on('send:vote', function(data) {
var ip = socket.handshake.headers['x-forwarded-for'] ||
socket.handshake.address.
Poll.findById(data.poll_id, function(err, poll) {
var choice = poll.choices.id(data.choice);
choice.votes.push({ ip: ip });
poll.save(function(err, doc) {
var theDoc = {
question: doc.question, _id: doc._id, choices: doc.choices,
userVoted: false, totalVotes: 0
for(var i = 0, ln = doc.choices. i & i++) {
var choice = doc.choices[i];
for(var j = 0, jLn = choice.votes. j & jLn; j++) {
var vote = choice.votes[j];
theDoc.totalVotes++;
theDoc.ip =
if(vote.ip === ip) {
theDoc.userVoted =
theDoc.userChoice = { _id: choice._id, text: choice.text };
socket.emit('myvote', theDoc);
socket.broadcast.emit('vote', theDoc);
};注意:如果您想知道为什么应用程序会在常规 API 地址属性的前面查找标头 'x-forwarded-for',因为这将确保当应用程序被部署到负载平衡环境中时,所使用的是正确的客户端 IP。如果您将该应用程序部署到 Bluemix 或 Cloud Foundry,这对于应用程序是否能正常工作至关重要。添加一个 Angular 服务将数据发送到 Web 套接字。Web Sockets 的后端功能现已创建完毕。目前剩下要做的工作是绑定前端,以发送和监听套接字事件。最佳方法是添加一个新的 Angular 服务。使用以下代码替换 public/javascripts 文件夹中的 services.js 文件:清单 20. public/javascripts/services.js
angular.module('pollServices', ['ngResource']).
factory('Poll', function($resource) {
return $resource('polls/:pollId', {}, {
query: { method: 'GET', params: { pollId: 'polls' }, isArray: true }
factory('socket', function($rootScope) {
var socket = io.connect();
on: function (eventName, callback) {
socket.on(eventName, function () {
var args =
$rootScope.$apply(function () {
callback.apply(socket, args);
emit: function (eventName, data, callback) {
socket.emit(eventName, data, function () {
var args =
$rootScope.$apply(function () {
if (callback) {
callback.apply(socket, args);
});最后,您需要编辑 PollItemCtrl 控制器,以便它能够监听和发送用于投票的 Web Socket
消息。将原始控制器替换为:清单 21. public/javascripts/controllers.js...
function PollItemCtrl($scope, $routeParams, socket, Poll) {
$scope.poll = Poll.get({pollId: $routeParams.pollId});
socket.on('myvote', function(data) {
console.dir(data);
if(data._id === $routeParams.pollId) {
$scope.poll =
socket.on('vote', function(data) {
console.dir(data);
if(data._id === $routeParams.pollId) {
$scope.poll.choices = data.
$scope.poll.totalVotes = data.totalV
$scope.vote = function() {
var pollId = $scope.poll._id,
choiceId = $scope.poll.userV
if(choiceId) {
var voteObj = { poll_id: pollId, choice: choiceId };
socket.emit('send:vote', voteObj);
alert('You must select an option to vote for');
...查看最终产品问卷调查应用程序现已创建完成。确保 mongod 仍在运行,并在 Eclipse 中再次运行 Node 应用程序。在浏览器中输入 http://localhost:3000,导航到一个问卷调查并进行投票。随后您就可以看到结果。要查看实时更新,请找到您的本地 IP 地址,并用该地址替换 localhost。在您的局域网中,使用不同的机器(甚至智能手机或平板电脑也可以)导航到这个地址。当您在另一个设备上进行投票时,结果会显示在该设备上,而且会自动发布到您的主要计算机浏览器上:图 4. 查看问卷调查结果下一步:进一步开发和部署您刚才创建的这个问卷调查应用程序是一个不错的起点,但还有很大的改进空间。在计划创建这类应用程序时,我喜欢使用一种敏捷方法来定义用户案例,并将项目划分为几块来实现。对于这个项目,我使用了 JazzHub,通过将项目的附属代码和源代码一起保存在一个云托管的存储库中,JazzHub 使得开发变得非常简单。如果您对您的应用程序感到很满意,下一步就是跟全世界的人分享它。在过去,即使部署一个非常简单的应用程序,可能也会是一场噩梦,但值得庆幸的是,那些日子已经一去不复返了。使用 IBM 新兴的兼容 Cloud Foundry 的
平台,您只需几分钟就可以通过最少的配置将您的应用程序部署到云中,一点都不麻烦。结束语这对于开发人员,现在是一个很好的时机。我们手头有大量框架和工具,它们使得开发大量应用程序不仅更简单、更快速,而且更加令人感到愉快。在本文中,您学习了如何使用被称为 MEAN 体系结构(Mongo、Express、Angular 和Node)的技术构建一个应用程序。该堆栈可能只需要一天时间就可以完成任务,远远超过了 LAMP 体系结构(Linux、Apache、MySQL 和 PHP),在 Web 应用程序开发和部署方面,该体系结构也许同样会超越 LAMP 体系结构。对我而言,我已经迫不及待跃跃欲试了。
相关主题:通过专门关于 Web 技术的文章和教程,扩展您在网站开发方面的技能。:这是有关 Ajax 编程模型信息的一站式中心,包括很多文档、教程、论坛、blog、wiki 和新闻。任何 Ajax 的新信息都能在这里找到。,这是有关 Web 2.0 相关信息的一站式中心,包括大量 Web 2.0 技术文章、教程、下载和相关技术资源。您还可以通过
栏目,迅速了解 Web 2.0 的相关概念。查看 ,了解更多和 HTML5 相关的知识和动向。
添加或订阅评论,请先或。
有新评论时提醒我
static.content.url=/developerworks/js/artrating/SITE_ID=10Zone=Web development, Open sourceArticleID=965871ArticleTitle=使用 Node.js、Express、AngularJS 和 MongoDB 构建一个实时问卷调查应用程序publish-date=

我要回帖

更多关于 nodejs权限管理系统 的文章

 

随机推荐