【金沙澳门官网网址】技术栈开发web应用

作者: 前端知识  发布:2019-07-26

用“MEAN”技术栈开发web应用(二)express搭建服务端框架

2015/11/14 · 基础技术 · MEAN

原文出处: 吕大豹   

上一篇我们讲了如何使用angular搭建起项目的前端框架,前端抽象出一个service层来向后端发送请求,后端则返回相应的json数据。本篇我们来介绍一下,如何在nodejs环境下利用express来搭建起服务端,使之正确的响应前端的请求。本文所讲的示例还是基于我们的学习项目QuestionMaker()

个人技术学习笔记,如有雷同,纯属正常,请勿喷,谢谢合作。

express 项目 最好不要全局安装 直接在当前目录下 npm install express --save
生成器 cnpm install express-generator -g
然后express -h 查看是否成功
利用express myapp 创建 cnpm install 安装依赖项 set DEBUG=myaoo & npm start 启动

路由(Routing)是由一个 URI(或者叫路径)和一个特定的 HTTP 方法(GET、POST 等)组成的,涉及到应用如何响应客户端对某个网站节点的访问。
每一个路由都可以有一个或者多个处理器函数,当匹配到路由时,这个/些函数将被执行。
路由的定义由如下结构组成:
app.METHOD(PATH, HANDLER)

  • app 是一个 express 实例
  • METHOD 是某个HTTP 请求方式中的一个
  • PATH 是服务器端的路径
  • HANDLER 是当路由匹配到时需要执行的函数
    // 对网站首页的访问返回 "Hello World!" 字样
    app.get('/', function (req, res) {
    res.send('Hello World!');
    });

// 网站首页接受 POST 请求
app.post('/', function (req, res) {
res.send('Got a POST request');
});

Express 内置的 express.static 可以方便地托管静态文件,例如图片、CSS、JavaScript 文件等。
将静态资源文件所在的目录作为参数传递给 express.static 中间件就可以提供静态资源文件的访问了。例如,假设在 public 目录放置了图片、CSS 和 JavaScript 文件,你就可以:app.use(express.static('public'));
如果你的静态资源存放在多个目录下面,你可以多次调用 express.static 中间件:
app.use(express.static('public'));
app.use(express.static('files'));

app.all() 是一个特殊的路由方法,没有任何 HTTP 方法与其对应,它的作用是对于一个路径上的所有请求加载中间件。
app.all('/secret', function (req, res, next) {
console.log('Accessing the secret section ...');
next(); // pass control to the next handler
});

路由路径和请求方法一起定义了请求的端点,它可以是字符串、字符串模式或者正则表达式。
字符串:
// 匹配 /about 路径的请求
app.get('/about', function (req, res) {
res.send('about');
});

// 匹配 /random.text 路径的请求
app.get('/random.text', function (req, res) {
res.send('random.text');
});
字符串模式:
// 匹配 abcd、abxcd、abRABDOMcd、ab123cd等
app.get('/abcd', function(req, res) {
res.send('ab
cd');
});
// 匹配 /abe 和 /abcde
app.get('/ab(cd)?e', function(req, res) {
res.send('ab(cd)?e');
});
正则:
// 匹配 butterfly、dragonfly,不匹配 butterflyman、dragonfly man等
app.get(/.fly$/, function(req, res) {
res.send('/.
fly$/');
});

运行起基于express的web服务器

express是一个web应用开发框架,它基于nodejs,扩展了很多web开发所需的功能,使得我们能够很方便的访问和操作request和response。请注意它和nginx或者tomcat并不是一个概念,它是一个开发框架,而不是服务器。

运行起基于express的web服务器是非常简单的,因为express都绑你封装好了。首先需要用npm安装好express,然后在项目根目录下新建一个server.js文件,内容如下:

JavaScript

var express = require('express'); var app = express(); app.listen(3000); var _rootDir = __dirname; var protectDir = _rootDir '/protect/'; app.use(express.static(_rootDir)); //注册路由 app.get('/', function(req, res){ res.sendFile(_rootDir '/src/index.html'); }); app.use(function(req, res, next) { res.status(404).sendFile(_rootDir '/src/404.html'); }); app.use(function(err, req, res, next) { console.error(err.stack); res.status(500).send('500 Error'); });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var express = require('express');
var app = express();
app.listen(3000);
 
var _rootDir = __dirname;
var protectDir = _rootDir '/protect/';
 
app.use(express.static(_rootDir));
 
//注册路由
app.get('/', function(req, res){
    res.sendFile(_rootDir '/src/index.html');
});
 
app.use(function(req, res, next) {
     res.status(404).sendFile(_rootDir '/src/404.html');
});
app.use(function(err, req, res, next) {
     console.error(err.stack);
     res.status(500).send('500 Error');
});

上述代码实现了这几个功能,首先创建了http服务器,监听在3000端口。

然后app.use(express.static(_rootDir));这一行是使用了静态文件服务的中间件,这样我们项目下的js、css以及图片等静态文件就都可以访问到了。

接下来是注册路由,此处只匹配一个路由规则,那就是”/”(网站的根目录),当匹配到此路由后把首页文件index.html直接用res.sendFile方法给发送到浏览器端。这样浏览器用

但是在本项目中,我们用的是angular的前端模板,所以后端就不需要模板了,没有进行配置。我们的路由机制也是完全使用的ng的前端路由,所以在express中只配置一条就够了。

在最后还有两块代码,分别是404和500错误的捕获。你可能会疑惑为什么是这样写呢?从上到下排下来就能分别捕获404和500了吗?其实这就是express的中间件机制,在此机制下,对客户端请求的处理像是一个流水线,把所有中间件串联起来,只要某个中间件把请求返回了,就结束执行,否则就从上到下一直处理此请求。

上面代码的流程就是,先按路由规则来匹配路径,如果路由匹配不到,则认为是发生404。500的错误请注意一个细节,在回调函数的参数中,第一个会传入err,就是错误对象,以此来标记是一个500错误。

路由句柄

可以为请求处理提供多个回调函数,其行为类似中间件。唯一的区别是这些回调函数有可能调用 next('route') 方法而略过其他路由回调函数。可以利用该机制为路由定义前提条件,如果在现有路径上继续执行没有意义,则可将控制权交给剩下的路径。
路由句柄有多种形式,可以是一个函数、一个函数数组,或者是两者混合,如下:
var cb0 = function (req, res, next) {
console.log('CB0');
next();
}

var cb1 = function (req, res, next) {
console.log('CB1');
next();
}

app.get('/example/d', [cb0, cb1], function (req, res, next) {
console.log('response will be sent by the next function ...');
next();
}, function (req, res) {
res.send('Hello from D!');
});

理解中间件

express的核心是中间件机制,通过使用各种中间件,能够实现灵活的组装我们所需的功能。中间件是在管道中执行的,所谓管道就是像流水线一样,每到达一个加工区,相应的中间件就可以处理request和response对象,处理完后再送往下一个加工区。如果某个加工区把请求终结了,比如调用send方法返回给了客户端,那么处理就终止了。大部分情况下,都有现成的中间件供我们使用,比如用body-parser解析请求实体,用路由(路由也是一种中间件)来正确的派发请求。

比如我们在server.js中添加如下的代码:

JavaScript

app.use(function(req, res, next){ console.log('中间件1'); next(); }); app.use(function(req, res, next){ console.log('中间件2'); });

1
2
3
4
5
6
7
8
app.use(function(req, res, next){
     console.log('中间件1');
     next();
});
 
app.use(function(req, res, next){
     console.log('中间件2');
});

我们添加了两个中间件,请求过来之后会先被第一个捕获,然后进行处理,输出“中间件1”。后面接着执行了next()方法,就会进入下一个中间件。一个中间件执行后只有两种选择,要么用next指向下一个中间件,要么将请求返回。如果什么都不做,请求将会被挂起,也就是说浏览器端将得不到返回,一直处于pendding状态。例如上面的中间件2,将会造成请求挂起,这是应该杜绝的。

响应方法

[res.end()] | 终结响应处理流程。 |
[res.redirect()] | 重定向请求。 |
[res.render()] | 渲染视图模板。 |
[res.send()] | 发送各种类型的响应。 |
[res.sendFile] | 以八位字节流的形式发送文件。 |
可使用 app.route() 创建路由路径的链式路由句柄。由于路径在一个地方指定,这样做有助于创建模块化的路由,而且减少了代码冗余和拼写错误。
app.route('/book')
.get(function(req, res) {
res.send('Get a random book');
})
.post(function(req, res) {
res.send('Add a book');
})
.put(function(req, res) {
res.send('Update the book');
});
可使用 express.Router 类创建模块化、可挂载的路由句柄。下面的实例程序创建了一个路由模块,并加载了一个中间件,定义了一些路由,并且将它们挂载至应用的路径上。
var express = require('express');
var router = express.Router();
// 该路由使用的中间件
router.use(function timeLog(req, res, next) {
console.log('Time: ', Date.now());
next();
});
// 定义网站主页的路由
router.get('/', function(req, res) {
res.send('Birds home page');
});
// 定义 about 页面的路由
router.get('/about', function(req, res) {
res.send('About birds');
});
module.exports = router;
然后在应用中加载路由模块:var birds = require('./birds'); ... app.use('/birds', birds);

路由设计

运行起了服务器,了解了中间件编程方式,接下来我们就该为前端提供api了。比如前端post一个请求到/api/submitQuestion来提交一份数据,我们该如何接收请求并做出处理呢,这就是路由的设计了。

给app.use的第一个参数传入路径可以匹配到对应的请求,例如:

JavaScript

app.use('/api/submitQuestion', function(){})

1
app.use('/api/submitQuestion', function(){})

这样就可以捕获到刚刚的提交试题的请求,在第二个参数中可以进行相应的处理,比如把数据插入到数据库。

但是,要注意了,express路由的正确使用姿势并不是这样的。app.use是用来匹配中间件的路径的,而不是请求的路径。因为路由也是一种中间件,所以这样的用法也是能够完成功能的,但是我们还是应该按照官方标准的写法来写。

标准的写法是什么样子呢?代码如下:

JavaScript

var apiRouter = express.Router(); apiRouter.post('/submitQuestion', questionController.save); app.use('/api', apiRouter);

1
2
3
var apiRouter = express.Router();
apiRouter.post('/submitQuestion', questionController.save);
app.use('/api', apiRouter);

我们利用的是express.Router这个对象,它同样有use、post、get等方法,用来匹配请求路径。然后我们再使用app.use把apiRouter作为第二个参数传进去。

要注意的是apiRouter.post和app.use的第一个参数。app.use匹配的是请求的“根路径”,这样可以把请求分为不同的类别,比如所有的异步接口我们都叫api,那么这类请求我们就都应该挂在“/api”下。按照这样的规则,我们整个项目的路由规则如下:

JavaScript

//注册路由 app.get('/', function(req, res){ res.sendFile(_rootDir '/src/index.html'); }); var apiRouter = express.Router(); apiRouter.post('/getQuestion', questionController.getQuestion); apiRouter.post('/getQuestions', questionController.getQuestions); apiRouter.post('/submitQuestion', questionController.save); apiRouter.post('/updateQuestion', questionController.update); apiRouter.post('/removeQuestion', questionController.remove); apiRouter.post('/getPapers', paperController.getPapers); apiRouter.post('/getPaper', paperController.getPaper); apiRouter.post('/getPaperQuestions', paperController.getPaperQuestions); apiRouter.post('/submitPaper', paperController.save); apiRouter.post('/updatePaper', paperController.update); apiRouter.post('/removePaper', paperController.remove); app.use('/api', apiRouter);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//注册路由
app.get('/', function(req, res){
    res.sendFile(_rootDir '/src/index.html');
});
 
var apiRouter = express.Router();
apiRouter.post('/getQuestion', questionController.getQuestion);
apiRouter.post('/getQuestions', questionController.getQuestions);
apiRouter.post('/submitQuestion', questionController.save);
apiRouter.post('/updateQuestion', questionController.update);
apiRouter.post('/removeQuestion', questionController.remove);
apiRouter.post('/getPapers', paperController.getPapers);
apiRouter.post('/getPaper', paperController.getPaper);
apiRouter.post('/getPaperQuestions', paperController.getPaperQuestions);
apiRouter.post('/submitPaper', paperController.save);
apiRouter.post('/updatePaper', paperController.update);
apiRouter.post('/removePaper', paperController.remove);
 
app.use('/api', apiRouter);

在router的第二个参数中,我们传入了questionController.save这样的方法,这是什么东西呢?怎么有点MVC的味道呢?没错,我们已经能够匹配到路由了,那服务端的业务逻辑以及数据库访问等该如何组织代码呢?

中间件

Express是由路由和中间件构成一个的 web 开发框架:从本质上来说,一个 Express 应用就是在调用各种中间件。
中间件Middleware是一个函数,它可以访问请求对象(request object), 响应对象(response object), 和 web 应用中处于请求-响应循环流程中的中间件,一般被命名为 next 的变量。
中间件的功能包括:
-执行任何代码。
-修改请求和响应对象。
-终结请求-响应循环。
-调用堆栈中的下一个中间件。
如果当前中间件没有终结请求-响应循环,则必须调用 next() 方法将控制权交给下一个中间件,否则请求就会挂起。

本文由金沙澳门官网发布于前端知识,转载请注明出处:【金沙澳门官网网址】技术栈开发web应用

关键词: 金沙澳门官网