项目实战(连载):基于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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容