为什么80%的码农都做不了架构师?>>>   

#0 系列目录#

  • 深入浅出Node.js系列
  • 【深入浅出Node.js系列一】什么是Node.js
  • 【深入浅出Node.js系列二】Node.js&NPM的安装与配置
  • 【深入浅出Node.js系列三】深入Node.js的模块机制
  • 【深入浅出Node.js系列四】Node.js的事件机制
  • 【深入浅出Node.js系列五】初探Node.js的异步I/O实现
  • 【深入浅出Node.js系列六】Buffer那些事儿
  • 【深入浅出Node.js系列七】Connect模块解析
  • 【深入浅出Node.js系列八】一个基于Node.js完整的Web应用实战
  • 【深入浅出Node.js系列九】一起撸Node.js
  • 【深入浅出Node.js系列十】一个简单的静态文件合并服务器
  • 【深入浅出Node.js系列十一】Node.js开发框架Express4.x

#1 建立项目# 让我们从头开始Express4.x的安装和使用吧,安装Node和NPM在本文就不多说了。Linux环境安装请参考文章,Node.js&NPM的安装与配置,Window环境安装直接下载Node的安装文件,双击安装就行了。

首先,我们需要安装express库。在Express3.6.x之前的版本,Express需要全局安装的,项目构建器模块是合并在Express项目中的,后来这个构建器被拆分出来,独立成为了一个项目express-generator,现在我们只需要全局安装express-generator项目就行了

~ npm install -g express-generator@4  #全局安装-g
C:\Users\Administrator\AppData\Roaming\npm\express -> C:\Users\Administrator\AppData\Roaming\npm\node_modules\express-ge
nerator\bin\expressexpress-generator@4.11.2 C:\Users\Administrator\AppData\Roaming\npm\node_modules\express-generator
├── sorted-object@1.0.0
├── commander@2.6.0
└── mkdirp@0.5.0 (minimist@0.0.8)

安装好express-generator包后,我们在命令行就可以使用express命令了。

~ express -V # 检查express的版本
4.11.2~ express -h  # 检查看express的帮助命令Usage: express [options] [dir]Options:-h, --help          output usage information-V, --version       output the version number-e, --ejs           add ejs engine support (defaults to jade)--hbs           add handlebars engine support-H, --hogan         add hogan.js engine support-c, --css   add stylesheet  support (less|stylus|compass) (defaults to plain css)--git           add .gitignore-f, --force         force on non-empty directory

接下来,我们使用express的命令,来创建项目了。

~ cd D:\workspace\javascript  # 进入工作目录~ D:\workspace\javascript>express -e nodejs-demo  # 创建项目create : nodejs-democreate : nodejs-demo/package.jsoncreate : nodejs-demo/app.jscreate : nodejs-demo/public/javascriptscreate : nodejs-demo/public/imagescreate : nodejs-demo/publiccreate : nodejs-demo/public/stylesheetscreate : nodejs-demo/public/stylesheets/style.csscreate : nodejs-demo/viewscreate : nodejs-demo/views/index.ejscreate : nodejs-demo/views/error.ejscreate : nodejs-demo/routescreate : nodejs-demo/routes/index.jscreate : nodejs-demo/routes/users.jscreate : nodejs-demo/bincreate : nodejs-demo/bin/wwwinstall dependencies:$ cd nodejs-demo && npm installrun the app:$ DEBUG=nodejs-demo:* ./bin/www

进入项目目录,下载依赖库,构建项目。

~ D:\workspace\javascript>cd nodejs-demo && npm install

启动项目。

~ D:\workspace\javascript\nodejs-demo>npm start> express4-demo@0.0.0 start D:\workspace\javascript\nodejs-demo
> node ./bin/wwwmodule.js:338throw err;^
Error: Cannot find module './routes/users'at Function.Module._resolveFilename (module.js:336:15)at Function.Module._load (module.js:278:25)at Module.require (module.js:365:17)at require (module.js:384:17)at Object. (D:\workspace\javascript\nodejs-demo\app.js:9:13)at Module._compile (module.js:460:26)at Object.Module._extensions..js (module.js:478:10)at Module.load (module.js:355:32)at Function.Module._load (module.js:310:12)at Module.require (module.js:365:17)

第一次启动发生了错误,可能是express-generator和express不匹配造成的,找到问题在app.js文件中,注释第9行和第26行。

..
//var users = require('./routes/users');
..
//app.use('/users', users);
..

再次启动项目。

D:\workspace\javascript\nodejs-demo>npm start
> express4-demo@0.0.0 start D:\workspace\javascript\nodejs-demo
> node ./bin/www

项目启动成功,打开浏览器 http://localhost:3000,就可以看到显示的页面了。

#2 目录结构# 接下来,我们详细看一下Express4项目的结构、配置和使用。

bin, 存放启动项目的脚本文件

node_modules, 存放所有的项目依赖库。

public,静态文件(css,js,img)

routes,路由文件(MVC中的C,controller)

views,页面文件(Ejs模板)

package.json,项目依赖配置及开发者信息

app.js,应用核心配置文件

#3 package.json项目配置# package.json用于项目依赖配置及开发者信息,scripts属性是用于定义操作命令的,可以非常方便的增加启动命令,比如默认的start,用npm start代表执行node ./bin/www命令。查看package.json文件。

{"name": "express4-demo","version": "0.0.0","private": true,"scripts": {"start": "node ./bin/www"},"dependencies": {"body-parser": "~1.10.2","cookie-parser": "~1.3.3","debug": "~2.1.1","ejs": "~2.2.3","express": "~4.11.1","morgan": "~1.5.1","serve-favicon": "~2.2.0"}
}

#4 app.js核心文件# 从Express3.x升级到Express4.x,主要的变化就在app.js文件中。查看app.js文件,我已经增加注释说明。

// 加载依赖库,原来这个类库都封装在connect中,现在需地注单独加载
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');// 加载路由控制
var routes = require('./routes/index');
//var users = require('./routes/users');// 创建项目实例
var app = express();// 定义EJS模板引擎和模板文件位置,也可以使用jade或其他模型引擎
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');// 定义icon图标
app.use(favicon(__dirname + '/public/favicon.ico'));
// 定义日志和输出级别
app.use(logger('dev'));
// 定义数据解析器
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
// 定义cookie解析器
app.use(cookieParser());
// 定义静态文件目录
app.use(express.static(path.join(__dirname, 'public')));// 匹配路径和路由
app.use('/', routes);
//app.use('/users', users);// 404错误处理
app.use(function(req, res, next) {var err = new Error('Not Found');err.status = 404;next(err);
});// 开发环境,500错误处理和错误堆栈跟踪
if (app.get('env') === 'development') {app.use(function(err, req, res, next) {res.status(err.status || 500);res.render('error', {message: err.message,error: err});});
}// 生产环境,500错误处理
app.use(function(err, req, res, next) {res.status(err.status || 500);res.render('error', {message: err.message,error: {}});
});// 输出模型app
module.exports = app;

我们看到在app.js中,原来调用connect库的部分都被其他的库所代替,serve-favicon、morgan、cookie-parser、body-parser,默认项目中,只用到了最基本的几个库,还没有其他需要替换的库,在本文最后有详细列出。

另外,原来用于项目启动代码也被移到./bin/www的文件,www文件也是一个node的脚本,用于分离配置和启动程序。查看./bin/www文件。

#!/usr/bin/env node   /*** 依赖加载*/
var app = require('../app');
var debug = require('debug')('nodejs-demo:server');
var http = require('http');/*** 定义启动端口*/
var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);/*** 创建HTTP服务器实例*/
var server = http.createServer(app);/*** 启动网络服务监听端口*/
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);/*** 端口标准化函数*/
function normalizePort(val) {var port = parseInt(val, 10);if (isNaN(port)) {return val;}if (port >= 0) {return port;}return false;
}/*** HTTP异常事件处理函数*/
function onError(error) {if (error.syscall !== 'listen') {throw error;}var bind = typeof port === 'string'? 'Pipe ' + port: 'Port ' + port// handle specific listen errors with friendly messagesswitch (error.code) {case 'EACCES':console.error(bind + ' requires elevated privileges');process.exit(1);break;case 'EADDRINUSE':console.error(bind + ' is already in use');process.exit(1);break;default:throw error;}
}/*** 事件绑定函数*/
function onListening() {var addr = server.address();var bind = typeof addr === 'string'? 'pipe ' + addr: 'port ' + addr.port;debug('Listening on ' + bind);
}

#5 Bootstrap界面框架# 创建Bootstrap界面框架,直接在index.ejs文件上面做修改。可以手动下载Bootstrap库放到项目中对应的位置引用,也可以通过bower来管理前端的Javascript库,参考文章 bower解决js的依赖管理。另外还可以直接使用免费的CDN源加载Bootstrap的css和js文件。下面我就直接使用bower来管理前端的JavaScript库的方式。编辑views/index.ejs文件:

<!DOCTYPE html>
<html lang="zh-CN"><head><title><%= title %></title><link rel="stylesheet" href="/bower_components/bootstrap/dist/css/bootstrap.min.css"><link rel='stylesheet' href='/stylesheets/style.css' /></head><body><div class="well jumbotron"><h1><%= title %></h1><p>This is a simple hero unit, a simple jumbotron-style component for calling extra attention to featured content or information.</p><p><a class="btn btn-primary btn-lg" href="#" role="button">Learn more</a></p></div><script src="/bower_components/jquery/dist/jquery.min.js"></script><script src="/bower_components/bootstrap/dist/js/bootstrap.min.js"></script></body>
</html>

效果如下,已经加入了bootstrap的样式了。

接下来,我们把index.ejs页面切分成3个部分:header.ejs, index.ejs, footer.ejs,用于网站页面的模块化

header.ejs, 为页面的头部区域

index.ejs, 为内容显示区域

footer.ejs, 为页面底部区域

编辑header.ejs:

<!DOCTYPE html>
<html lang="zh-CN"><head><title><%= title %></title><link rel="stylesheet" href="/bower_components/bootstrap/dist/css/bootstrap.min.css"><link rel='stylesheet' href='/stylesheets/style.css' /></head><body>

编辑footer.ejs:

    <script src="/bower_components/jquery/dist/jquery.min.js"></script><script src="/bower_components/bootstrap/dist/js/bootstrap.min.js"></script></body>
</html>

编辑index.ejs:

<% include header.ejs %><div class="well jumbotron">
<h1><%= title %></h1>
<p>This is a simple hero unit, a simple jumbotron-style component for calling extra attention to featured content or information.</p>
<p><a class="btn btn-primary btn-lg" href="#" role="button">Learn more</a></p>
</div><% include footer.ejs %>

把页表和页底的代码分离后,让index.ejs页面的核心代码更少,更容易维护。

#6 路由功能# 路由功能,是Express4以后全面改版的功能。在应用程序加载隐含路由中间件,不用担心在中间件被装载相对于路由器中间件的顺序。定义路由的方式是不变的,路由系统中增加2个新的功能。

app.route()函数,创建可链接的途径处理程序的路由路径。

express.Router类,创建模块化安装路径的处理程序。

app.route方法会返回一个Route实例,它可以继续使用所有的HTTP方法,包括get,post,all,put,delete,head等。

app.route('/users').get(function(req, res, next) {}).post(function(req, res, next) {})

express.Router类,则可以帮助我们更好的组织代码结构。在app.js文件中,定义了app.use(‘/’, routes); routes是指向了routes目录下的index.js文件,./routes/index.js文件中,express.Router被定义使用,路径/*处理都会由routes/index.js文件里的Router来处理。如果我们要管理不同的路径,那么可以直接配置为多个不同的Router。

app.use('/user', require('./routes/user').user);
app.use('/admin', require('./routes/admin').admin);
app.use('/', require('./routes'));

#7 Ejs模板使用# 让ejs模板文件,使用扩展名为html的文件。修改:app.js

app.engine('.html', ejs.__express);
app.set('view engine', 'html');
// app.set('view engine', 'ejs');

修改后,ejs变量没有定义,supervisor的程序会一直报错

ReferenceError: ejs is not defined
at Object. (D:\workspace\project\nodejs-demo\app.js:17:21)
at Module._compile (module.js:456:26)
at Object.Module._extensions..js (module.js:474:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:312:12)
at Function.Module.runMain (module.js:497:10)
at startup (node.js:119:16)
at node.js:901:3
DEBUG: Program node app.js exited with code 8

在app.js中增加ejs变量:

var express = require('express')
, routes = require('./routes')
, user = require('./routes/user')
, http = require('http')
, path = require('path')
, ejs = require('ejs');

#8 Session使用# session这个问题,其实是涉及到服务器的底层处理方式。像Java的web服务器,是多线程调用模型。每用户请求会打开一个线程,每个线程在内容中维护着用户的状态。

像PHP的web服务器,是交行CGI的程序处理,CGI是无状态的,所以一般用cookie在客户的浏览器是维护用户的状态。但cookie在客户端维护的信息是不够的,所以CGI应用要模仿用户session,就需要在服务器端生成一个session文件存储起来,让原本无状态的CGI应用,通过中间文件的方式,达到session的效果。

Nodejs的web服务器,也是CGI的程序无状态的,与PHP不同的地方在于,单线程应用,所有请求都是异步响应,通过callback方式返回数据。如果我们想保存session数据,也是需要找到一个存储,通过文件存储,redis,Mongdb都可以。

接下来,我将演示如何通过mongodb来保存session,并实现登陆后用户对象传递。

app.js文件:

var express = require('express')
, routes = require('./routes')
, user = require('./routes/user')
, http = require('http')
, path = require('path')
, ejs = require('ejs')
, SessionStore = require("session-mongoose")(express);var store = new SessionStore({url: "mongodb://localhost/session",interval: 120000
});....
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.cookieParser());
app.use(express.cookieSession({secret : 'fens.me'}));app.use(express.session({secret : 'fens.me',store: store,cookie: { maxAge: 900000 }
}));app.use(function(req, res, next){res.locals.user = req.session.user;next();
});
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));

注:app.js文件有顺序要求,一定要注意!!!

安装session-mongoose依赖库:

D:\workspace\project\nodejs-demo>npm install session-mongoose
D:\workspace\project\nodejs-demo\node_modules\session-mongoose\node_modules\mongoose\node_modules\mongodb\node_modules\bson>node "D:\toolkit\nodejs\node_modules\npm\bin\node-gyp-bin\\..\..\node_modules\node-gyp\bin\node-gyp.js" rebuild
C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\Microsoft.Cpp.InvalidPlatform.Targets(23,7): error MSB8007: 项目“kerberos.vcxproj”的平台无效。平台为“x64”。您会看到此消息的可能原因是,您尝试在没有解决方案文件的情况下生成项目,并且为
oose\node_modules\mongoose\node_modules\mongodb\node_modules\bson\build\bson.vcxproj]
session-mongoose@0.2.2 node_modules\session-mongoose
└── mongoose@3.6.10 (mpath@0.1.1, ms@0.1.0, hooks@0.2.1, sliced@0.0.3, muri@0.3.1, mpromise@0.2.1, mongodb@1.3.3)

安装有错误但是没关系。访问:http://localhost:3000/login,正常

修改routes/index.js文件,exports.doLogin方法:

exports.doLogin = function(req, res){var user={username:'admin',password:'admin'}if(req.body.username===user.username && req.body.password===user.password){req.session.user=user;return res.redirect('/home');} else {return res.redirect('/login');}
};

exports.logout方法:

exports.logout = function(req, res){req.session.user=null;res.redirect('/');
};

exports.home方法:

exports.home = function(req, res){res.render('home', { title: 'Home'});
};

这个时候session已经起作用了,exports.home的user显示传值已经被去掉了。 是通过app.js中app.use的res.locals变量,通过框架进行的赋值。

app.use(function(req, res, next){res.locals.user = req.session.user;next();
});

注:这个session是express3.0的写法,与express2.x是不一样的。原理是在框架内每次赋值,把我们刚才手动传值的过程,让框架去完成了

转载于:https://my.oschina.net/xianggao/blog/604563

【深入浅出Node.js系列十一】Node.js开发框架Express4.x相关推荐

  1. Node.js系列之node.js初探

    官方介绍:Node.js is a platform built on Chrome's JavaScript runtime for easily building fast, scalable n ...

  2. js 打印数组_Node.js系列二 - Node基础知识

    一. Node执行代码 1.1. JavaScript文件执行 如果我们编写一个js文件,里面存放JavaScript代码,如何来执行它呢? // 1.直接打印一段文字 console.log(&qu ...

  3. js 多个定时器_Node.js系列深入浅出Node模块化开发——CommonJS规范

    前言 本文将为大家透彻的介绍关于Node的模块化--CommonJS的一切. 看完本文可以掌握,以下几个方面: 什么是模块化,以及没有模块化会带来哪些问题,是如何解决的: JavaScript的设计缺 ...

  4. Node 系列 - 003 - commander.js

    ----------☆☆☆---------- Node 系列相应地址: 代码仓库:https://github.com/LiangJunrong/all-for-one 文章仓库:https://g ...

  5. Node.js 系列:构建原生 Node.js 应用

    原生 Node.js 应用 Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境 Node.js 使用了一个事件驱动.非阻塞式 I/O 的模型,使其轻量又高效 Nod ...

  6. 【node】express的www.js文件里面的process.env.PORT

    问题描述 在分析www.js里面的代码的时候,有: var port = normalizePort(process.env.PORT || '3001'); app.set('port', port ...

  7. Node.js-sublime text3 配置node.js(ERROR: The process node.exe not found.)

    默认已经安装好sublime.node和npm 1.sublime的node.js插件下载 由于在package control上经常下载失败,所以这里直接从GitHub上进行下载! GitHub下载 ...

  8. node JS獲取GPS_Node.js 14 正式发布:V8 引擎升级,新增异步本地存储 API

    Node.js 14 版本于近日正式发布, 此版本包含的亮点如下: 对诊断功能的改进 升级 v8 引擎 新增实验性的异步本地存储 API 强化流 API 移除实验性模块中的警告 移除一部分早期版本中废 ...

  9. [js高手之路]深入浅出webpack教程系列9-打包图片(file-loader)用法

    [js高手之路]深入浅出webpack教程系列索引目录: [js高手之路]深入浅出webpack教程系列1-安装与基本打包用法和命令参数 [js高手之路]深入浅出webpack教程系列2-配置文件we ...

最新文章

  1. Oracle根据日期区间查询Date类型的数据
  2. 教您用CT数据和桌面3D打印机打印自己的器官模型
  3. Spark笔记:RDD基本操作(下)
  4. html5中表格如何等分,纯css3饼图五等分
  5. 转为字符数组_数组的20种常用的方法?
  6. 《Code:The Hidden Language Of Computer Hardware and Software》 ——笔记
  7. 查看java的dump日志并进行分析
  8. 读书笔记 - 《皇上走了》
  9. 初学者|一起来看看词性标注
  10. UCINET软件使用简介——主菜单简介2
  11. 在线画图工具ProcessOn
  12. itools苹果录屏大师_【智慧技术】上网课没有手写板怎么办?AirPlayer(苹果录屏大师)秒将苹果手机“操作界面quot;或quot;摄像头quot;投屏到电脑上...
  13. 几何公差(GDT)的特征项目及符号
  14. 【C++】将小写阿拉伯数字转换为大写汉字数字(将数字用中文的方法读出来)
  15. 角度转换之度分秒的转换成度
  16. Android12 源码下载、编译、刷机、单编调试Framework
  17. 作数学题应不该用计算机,【中考备考】最新关于初一年级数学题在线解答汇总讲解...
  18. 百度地图API权限部分
  19. Mysql cancel分析
  20. Java实现 LeetCode 526 优美的排列(DFS)

热门文章

  1. 公司--》字符串截取
  2. mysql Sql slow log_MySQL慢查询日志(SLOW LOG)
  3. 380v pcb 接线端子_连接器、接线端子、插针插孔三者究竟有什么区别?
  4. java 取得textfield_怎样获取java中textfield的内容
  5. 文档管理服务器文件的脱机编辑选项无法编辑,让MOSS2007文档的存取更具个性
  6. 征途单机版场景服务器端口被占用,模拟城市5解决端口被占用的方法
  7. 山社电机: SAMSR -外部接口测试
  8. c语言逻辑运算类指令,组成原理第二章——计算机指令
  9. 跳至下一个断点_基金经理:DeFi将推动以太坊在下一个上涨周期中涨至9000美元...
  10. eltable刷新整个表格方法_Word表格函数计算怎么做?都在这篇!