Express是一个精简、灵活的Node.js的Web应用程序开发框架,为Web和移动应用程序提供了一组强大的功能。它是Node.js中流行的Web开发框架,被大多数开发人员所使用。使用Express可以快速地开发一个Web应用,其他的开发框架也都是基于Express构建的。
要安装Express,首先要具备Node.js环境,也就是说你已经安装了Node.js。
安装Express非常简单,使用Node.js的配套工具npm命令即可安装:
$ npm install -g express-generator
npm命令运行完毕,再运行命令:
$ express --version
如果能够看到Express的版本号,证明Express已经安装成功。截至本书写完,Express的版本号是4.16.0。
$ express --version
$ 4.16.0
其实这里安装的是一个应用生成器工具——express-generator ,通过它可以快速创建一个应用的骨架,为快速创建Node.js项目提供便利。
在安装完Express之后,就可以使用Express命令来创建一个新的项目了。
使用Express创建项目非常简单,具体步骤如下:
(1)按WIN R键打开“运行”对话框,输入cmd命令,单击“确定”按钮打开命令行窗口,如图1-1所示。
图1-1 命令行窗口
(2)进入工作目录,可以自定义一个工作目录,如下:
$ e:
$ cd express/code
(3)执行,创建一个名为hello的Express项目:
$ express hello
(4)此时可以看到它会自动执行,图1-2代表创建成功。
图1-2 Express创建hello项目
(5)创建成功之后会在code目录下出现一个名叫hello的目录,进入hello目录,然后安装依赖包:
$ cd hello
$ npm install
(6)安装完毕之后,执行命令启动应用:
$ npm start
(7)应用启动后,在浏览器中输入http://localhost:3000/ 网址就可以看到名叫hello的这个应用了,如图1-3所示。
图1-3 Express默认应用启动界面
项目正常启动后,我们用开发工具打开hello项目看一下它的目录结构,如图1-4所示。
图1-4 Express默认应用的目录结构
项目结构不太复杂是不是?相比较其他语言的框架来说很轻量,这也是Node.js快速开发的一个特色。
目录结构中的文件及其作用如表1-1所示。
表1-1 Express默认应用中的文件及其作用
目录名/文件名 |
类 型 |
作 用 |
bin |
目录 |
服务器脚本默认目录 |
bin/www.js |
文件 |
服务器默认脚本,即启动服务脚本 |
node_modules |
目录 |
依赖包安装目录,用于存放依赖包 |
public |
目录 |
静态资源目录,用于存放静态资源 |
routes |
目录 |
路由目录,用于存放路由文件 |
routes/index.js |
文件 |
首页路由文件 |
routes/users.js |
文件 |
用户路由文件 |
views |
目录 |
页面目录,用于存放页面文件 |
views/error.jade |
文件 |
错误页面 |
views/index.jade |
文件 |
首页 |
views/layout.jade |
文件 |
页面共用布局 |
app.js |
文件 |
应用主文件 |
package.json |
文件 |
项目配置文件 |
package-lock.json |
文件 |
锁定的项目配置文件 |
app.js文件相当于项目启动的主入口文件,有一些公共方法和服务器配置等信息。代码分析如下:
var createError = require('http-errors'); // http错误处理模块
var express = require('express'); // 引入Express
var path = require('path'); // 引入path
var cookieParser = require('cookie-parser'); // 引入cookie处理对象
var logger = require('morgan'); // 引入日志模块
var indexRouter = require('./routes/index'); // 引入路由目录中的index.js文件
var usersRouter = require('./routes/users'); // 引入路由目录中的users.js文件
var app = express(); // 创建Express应用
app.set('views', path.join(__dirname, 'views')); // 定义页面目录
app.set('view engine', 'jade'); // 定义页面模板引擎
app.use(logger('dev')); // 定义日志打印级别
app.use(express.json()); // 定义JSON格式处理数据
// 定义使用urlencode处理数据及querystring模块解析数据
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser()); // 定义使用cookie处理对象
// 定义静态资源目录public
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', indexRouter); // 定义指向index.js的路由
app.use('/users', usersRouter); // 定义指向users.js的路由
// 定义404错误处理
app.use(function(req, res, next) {
next(createError(404));
});
// 定义其他错误处理
app.use(function(err, req, res, next) {
// 设置locals,只在开发环境生效
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
res.status(err.status || 500); // 返回错误http状态码
res.render('error'); // 渲染错误页面
});
module.exports = app;
接触到一款框架,首先要熟悉的就是它的路由。路由是指应用程序的端点(URI)如何响应客户端请求。通俗来说,就是定义什么路径来访问。
在Express中定义路由特别简单。首先来看一下routes目录中的index.js路由文件,也就是首页路由文件,它是一个GET请求路由,如下:
var express = require('express'); // 引入Express
var router = express.Router(); // 引入Express路由对象
//首页路由
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
module.exports = router; // 导出路由
首页路由文件很简单,其中也只定义了一个根目录的路由。
将其中的代码:
res.render('index', { title: 'Express' });
更换成如下代码:
res.render('index', { title: Hello});
保存文件,重新运行npm start命令,用浏览器打开http://localhost:3000查看效果,因为在浏览器地址栏中输入地址发送的请求默认是GET请求,所以可以得到如图1-5所示的结果。
可以看到,之前的Express被换成了Hello。
前面的一段代码定义了首页的路由,这个路由定义的是GET请求,请求的是“/”路径,也就是根路径。接着后面跟着一个方法,当用户发送GET请求根路径的时候,就进入这个方法中,在这个方法中可以进行一些对请求的操作。
图1-5 将Express更改成Hello的运行效果
Express默认的首页方法中使用了res.render()方法,该方法的作用是渲染页面。其个参数就是页面的文件名,这里对应的就是views目录中的index.jade文件;第二个参数是传入到页面文件的对象,可以将数据渲染到页面上。
分析过首页路由之后,可以试着自己写一个路由。在index.js路由文件中添加如下代码:
// 定义一个GET请求“/world”的路由,执行方法
router.get('/world', function(req, res, next) {
res.render('index', { title: 'Hello World' }); // 渲染index页面
});
将项目重新启动,打开浏览器请求地址http://localhost:3000/world,会看到新的页面,如图1-6所示。
图1-6 Hello World路由演示效果
新定义的路由生效了,是不是很简单呢?
但是每次更改过路由文件都要重新启动项目才会生效,如此,开发效率不太高。在这里,给大家推荐一个工具——nodemon,其安装方式也是利用npm命令:
npm install -g nodemon
安装完成之后,修改项目根目录中的package.json文件,将其中的
"scripts": {
"start": "node ./bin/www"
},
修改成:
"scripts": {
"start": "nodemon ./bin/www" // 使用nodemon运行
},
然后再执行npm start命令启动项目,这样在路由文件被更改并保存之后,会自动重启项目,并可以立刻在浏览器中看到更改后的运行结果。
HTTP请求方式除了GET之外,还有POST、PUT等其他方式。类似于GET请求的路由编写方式,其他请求方式也换成相应的router方法,如下:
// POST请求方式
router.post('/world', function(req, res, next) {
res.render('index', { title: 'Hello World' }); // 渲染index页面
});
// PUT请求方式
router.put('/world', function(req, res, next) {
res.render('index', { title: 'Hello World' }); // 渲染index页面
});
// DELETE请求方式
router.delete('/world', function(req, res, next) {
res.render('index', { title: 'Hello World' }); // 渲染index页面
});
大家可以自己尝试一下相应的请求方式。
前面演示的路由匹配都是完整匹配,即定义“/world”,在浏览器中要输入“/world”才能匹配到。
在Express中,除了完整匹配,还支持模糊匹配,例如:
router.get('/wes?t', function(req, res, next) {
res.render('index', { title: 'Hello World' });
});
在浏览器中查看,会发现当请求http://localhost:3000/west和http://localhost:3000/wet时都可以成功。
同样,模糊匹配还可以按如下处理:
// 能够匹配/west、/weest、/weeest等
router.get('/we st', function (req, res, next) {
res.render('index', { title: 'Hello World' });
})
// 能够匹配/west、/wt
router.get('/w(es)?t', function (req, res, next) {
res.render('index', { title: 'Hello World' });
})
// 能够匹配/west、/we123st、/wexxxst等
router.get('/we*st', function (req, res, next) {
res.render('index', { title: 'Hello World' });
})
// 能够匹配/west、/we123st、/wexxxst等
router.get('/we*st', function (req, res, next) {
res.render('index', { title: 'Hello World' });
})
同时,Express路由还支持正则表达式,如下:
// 能够匹配路径中包含west的任何内容,如/west、/aawest、/westee等
router.get(/west/, function (req, res, next) {
res.render('index', { title: 'Hello World' });
})
在项目开发过程中,有时会需要用共同的方法来拦截请求,比如需要对请求做一些身份验证。
如果接口是一个或两个的话,可以在某个接口的处理方法中进行验证,但是如果很多接口都需要进行验证的话,就不可能在每一个需要验证的接口上都添加一套代码,那样既费时又费力。为了提高效率,需要将验证的方法单独提出来。
Express提供了一个很好的工具,即中间件。可以在中间件中定义一个验证方法,然后在需要验证的接口路由上添加验证中间件,完成接口的验证。
那么什么是中间件呢?这里的中间件是一些处理方法的合集。Express其实就是一个路由和中间件合成的Web框架。
在前面说过,当前端请求路由的时候,会进入后面的路由处理方法中打开前面的index.js路由文件,查看根路径的路由:
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
当前端使用GET方法请求根路径的时候,会进入第二个参数function中,而这个路由处理方法就是Express中的中间件。
Express的中间件函数可以让我们访问请求对象Request、响应对象Response及下一个中间件函数。其中,下一个中间件函数通常声明为next。
在Express中,中间件会被Express传入3个参数,如表1-2所示。
表1-2 Express中间件的3个参数
参 数 顺 序 |
参 数 |
描 述 |
1 |
req |
请求数据对象Request |
2 |
res |
返回数据对象Response |
3 |
next |
下一步函数 |
为了实现上述验证功能,可以在路由处理方法之前也就是中间件之前再加一个方法,例如:
// 定义一个GET请求
router.get('/car', function(req, res, next){
console.log ('这里是中间件'); // 打印日志,方便查看
next(); // 继续下一步,下一个中间件
}, function(req, res, next) {
res.send('这里是返回'); // 向页面发送数据
});
在router.get()方法中,在前面定义的路由处理方法之前又传入了一个方法,这时使用浏览器访问http://localhost:3000/car,会看到控制台中打印出“这里是中间件”,同时浏览器的页面上展示出“这里是返回”。
在中间件中可以做很多事情,在后面的实战项目演示中大家将会经常见到它。