项目实战(连载):基于Angular2+Mongodb+Node技术实现的多用户博客系统教程(5)

本章主要讲什么(一句话)?

《项目实战:基于Angular2+Mongodb+Node技术实现的多用户博客系统教程(5)》

                                                                             -- Express框架代码培析&优化配置

一、前言

     上一章,我们给大家讲解了项目实战所必须的基础环境搭建与必备知识储备技能,同时对Express框架进行了快速搭建,本章我们将会对Express框架给大家做一个详细的培析,同时对本框架结合我们的项目需求做一优化重构。

PS--不过后继想改变一下严肃的风格,采用一些好玩的图片配合,希望能让枯燥的编程能够产生点乐趣 :)

二、Express框架详解

2.1、工程结构

2.1.1、工程结构-工程目录

生成的工程目录里面都有什么,打开我们的blog文件夹,里面如图所示:

说明如下:

app.js:启动文件,或者说入口文件

package.json:存储着工程的信息及模块依赖,当在dependencies中添加依赖的模块时,运行npm install,npm会检查当前目录下的package.json,并自动安装所有指定的模块

node_modules:存放package.json中安装的模块,当你在package.json添加依赖的模块并安装后,存放在这个文件夹下

public:存放image、css、js等文件

routes:存放路由文件

views:存放视图文件或者说模版文件

bin:存放可执行文件

2.1.2、工程结构-app.js

打开app.js,让我们看看里面究竟有什么:

核心代码我以注释的方式解释!

// require()加载了express、path等模块,以及routes文件夹下

//的index. js和users.js路由文件

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();//生成一个express实例app。

/*设置views文件夹为存放视图文件的目录,即存放模板文件的地方

,__dirname为全局变量,存储当前正在执行的脚本所在的目录。*/

app.set('views', path.join(__dirname, 'views'));

//设置视图模板引擎为ejs

app.set('view engine', 'ejs');

// uncomment after placing your favicon in /public

//设置/public/favicon.ico为favicon图标。

//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));

app.use(logger('dev')); //加载日志中间件

app.use(bodyParser.json()); //加载解析json的中间件

app.use(bodyParser.urlencoded({ extended: false })); //加载解析urlencoded请求体的中间件

app.use(cookieParser()); //加载解析cookie的中间件

app.use(express.static(path.join(__dirname, 'public'))); //设置public文件夹为存放静态文件的目录。

//路由控制器

app.use('/', routes);

app.use('/users', users);

// catch 404 and forward to error handler

//捕获404错误,并转发到错误处理器

app.use(function(req, res, next) {

var err = new Error('Not Found');

err.status = 404;

next(err);

});

// error handlers

// development error handler

// will print stacktrace

//开发环境下的错误处理器,将错误信息渲染error模版并显示到浏览器中

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

});

});

}

// production error handler

// no stacktraces leaked to user

//生产环境下的错误处理器,将错误信息渲染error模版并显示到浏览器中

app.use(function(err, req, res, next) {

res.status(err.status || 500);

res.render('error', {

message: err.message,

error: {}

});

});

//导出app实例供其他模块调用

module.exports = app;

2.1.3、工程结构-bin/www文件

打开bin目录下的www文件,内容如下:


#!/usr/bin/env node

/**

* Module dependencies.

*/

var app = require('../app');

var debug = require('debug')('blog:server');

var http = require('http');

/**

* Get port from environment and store in Express.

*/

var port = normalizePort(process.env.PORT || '3000');

app.set('port', port);

/**

* Create HTTP server.

*/

var server = http.createServer(app);

/**

* Listen on provided port, on all network interfaces.

*/

server.listen(port);

server.on('error', onError);

server.on('listening', onListening);

。。。。


上述代码解释如下:

(1) #!/usr/bin/env node:表明是node可执行文件。

(2) var debug =require('debug')('blog:server')

:引入debug模块,打印调试日志。

(3) var app = require('../app’):引入我们上面导出的app实例。

(4) app.set('port', process.env.PORT || 3000):设置端口号。

(5) var server = http.createServer(app); server.listen(port);

server.on('error', onError); server.on('listening',onListening);

启动工程并监听3000端口

2.1.4、工程结构-routes/index.js文件

再看routes/index.js文件:

var express = require('express');

var router = express.Router();

/* GET home page. */

router.get('/', function(req, res, next) {

res.render('index', { title: 'Express' });

});

module.exports = router;

生成一个路由实例用来捕获访问主页的GET请求,导出这个路由并在app.js中通过app.use('/', routes);加载。这样,当访问主页时,就会调用res.render('index', { title: 'Express' });渲染views/index.ejs模版并显示到浏览器中。

我们再看看views/index.ejs文件:

在渲染模板时我们传入了一个变量title值为express字符串,模板引擎会将所有<%= title %>替换为express,然后将渲染后生成的html显示到浏览器中,如上图所示

至此,我们知道了如何创建一个Express工程并启动它,了解了工程的大体结构和运作流程!

2.2、路由控制

2.2.1、工作原理

routes/index.js中有以下代码:

router.get('/', function(req, res){

res.render('index', { title: 'Express' });

});

这段代码的意思是当访问主页时,调用ejs模板引擎,来渲染index.ejs模版文件(即将title变量全部替换为字符串Express),生成静态页面并显示在浏览器中。

我们来作一些修改,以上代码实现了路由的功能,我们当然可以不要routes/index.js文件,把实现路由功能的代码都放在app.js里,但随着时间的推移app.js会变得臃肿难以维护,这也违背了代码模块化的思想,所以我们把实现路由功能的代码都放在routes/index.js里。官方给出的写法是在app.js中实现了简单的路由分配,然后再去index.js中找到对应的路由函数,最终实现路由功能。我们不妨把路由控制器和实现路由功能的函数都放到index.js里,app.js中只有一个总的路由接口。

最终将app.js修改为:

PS:注意加粗部分!


……

var http = require('http');

……

var routes = require('./routes/index');

var app = express();

app.set('port', process.env.PORT || 3000);

app.set('views', path.join(__dirname, 'views'));

app.set('view engine', 'ejs');

….

app.use(cookieParser());

app.use(express.static(path.join(__dirname, 'public')));

routes(app);

http.createServer(app).listen(app.get('port'),function(){

console.log('服务器已启动,监听端口:%s',app.get('port'));

});

//app.use('/', routes);

//app.use('/users', users);

// catch 404 and forward to error handler

app.use(function(req, res, next) {

…..


<%= title %>

修改index.js如下:

module.exports = function(app) {

app.get('/', function (req, res) {

res.render('index', { title: 'Express' });

});

};

现在,再运行你的app,你会发现主页毫无二致。这里我们在routes/index.js中通过module.exports导出了一个函数接口,在app.js中通过require加载了index.js然后通过routes(app)调用了index.js导出的函数。

同时删除掉:bin目录及目录下的www文件

2.2.2、路由规则

Ø 通过Requre获取数据

express封装了多种http请求方式,我们主要只使用get和post两种,即app.get()和app.post()。

app.get()和app.post()的第一个参数都为请求的路径,第二个参数为处理请求的回调函数,回调函数有两个参数分别是req和res,代表请求信息和响应信息 。路径请求及对应的获取路径有以下几种形式:

方式一、req.query

// GET /search?q=fwytech+ferret

req.query.q

// => "fwytech ferret"

// GET /shoes?order=desc&color=blue& type=converse

req.query.order

// => "desc"

req.query.shoe.color

// => "blue"

req.query.shoe.type

// => "converse"

方式二、req.body

// POST user[name]=fwytech&user[email]=fwytech@126.com

req.body.user.name

// => "fwytech"

req.body.user.email

// => "fwytech@126.com"

// POST { "name": "fwytech" }

req.body.name

// => "fwytech"

方式三、req.params

// GET /user/tj

req.params.name

// => "tj"

// GET /file/javascripts/jquery.js

req.params[0]

// => "javascripts/jquery.js"

方式四、req.param(name)

// ?name=tobi

req.param('name')

// => "tobi"

// POST name=tobi

req.param('name')

// => "tobi"

// /user/tobifor/user/:name

req.param('name')

// => "tobi"

不难看出:

·req.query: 处理get请求,获取get请求参数

·req.params: 处理/:xxx形式的get或post请求,获取请求参数

·req.body: 处理post请求,获取post请求体

·req.param(): 处理get和post请求,但查找优先级由高到低为req.params→req.body→req.query

路径规则还支持正则表达式,更多请查阅:http://expressjs.com/en/api.html

2.2.3、路由规则添加路由规则

请在Node命令提示符下将目录定位至你的node-blog目录下:

> node app

启动node服务器!

再次打开浏览器:健入http://localhost:3000,如果一切正常将会显示如下:

当我们访问localhost:3000/hello这种不存在的页面时就会显示:

这是因为不存在/hello的路由规则,而且它也不是一个public目录下的文件,所以express返回了404 Not Found的错误。下面我们来添加这条路由规则,使得当访问localhost:3000/hello时,页面显示hello,world!

注意:以下修改仅用于测试,看到效果后再把代码还原回来。

修改index.js,在app.get('/')函数后添加一条路由规则:

app.get('/hello', function (req, res) {

res.send('hello,world!');

});

重启之后,访问localhost:3000/hello页面显示如下:

三、后述

   《基于Angular2+Mongodb+Node技术实现的多用户博客系统》正在连载中,明天我将为大家推出【第六章:项目需求分析&路由归划&MongoDB配置】,欢迎各位继续关注~

    搜索并关注“风舞烟”的简书专栏、头条号、微信公众号、 企鹅媒体平台,你可以定期收到关于简书专栏的最新动态以及IT前沿最新技术的高质量经验文章、视频分享。

    谢谢大家的支持,欢迎大家留言交流。

本章代码下载:http://pan.baidu.com/s/1nu6EIMH

推荐阅读更多精彩内容