Express的官方描述是:一个基于Node.js平台的快速,开放,极简的web框架。这句话中有两个关键词:web
总而言之,Express其实就是在Node内置的HTTP模块上构建了一层抽象,理论上全部Express可以实现的功能, 均可以经过Node来实现。相比于Node,Express具备的优点:数据库
中间件(Middleware) 是一个函数,它能够访问请求对象(request), 响应对象(response), 和 web 应用中处于请求-响应循环流程中的 next的变量。接下来咱们先编写一个简单的express应用,看看究竟中间件是什么?express
const express = require('express');
const app = express(); const fn = (req, res, next) => { console.log("hello,world"); }; app.use(fn) const port = 3000; app.listen(port,() => { console.log(`应用运行在${port}端口`); }) 复制代码
当咱们访问localhost:3000时就会发现,打印出了函数fn中的hello,world。这里的函数fn就是一个中间件,app.use(fn)就是运行这个中间件,实现他的功能。事实上,整个Express的功能就是由中间件组成,中间件具备的功能包括:编程
咱们经过代码看一下中间件的这些功能:cookie
const express = require('express');
const app = express(); // 第一个中间件 app.use((req, res, next) => { console.log("中间件1"); next();// 若是没有next则会被挂起 }) // 第二个中间件 app.use((req, res, next) => { console.log("中间件2"); //继续执行第二个中间件 next(); }) // 第三个中间件 app.use((req, res, next) => { console.log("中间件3"); res.send('hello'); // 终结请求-响应循环 }) // 第四个中间件 app.use((req, res, next) => { console.log("中间件4"); // 中介后的中间件再也不执行 }); const port = 3000; app.listen(port,() => { console.log(`应用运行在${port}端口`); }) 复制代码
最终的输出结果为:中间件1,中间件2,中间件3
。从上面的代码中咱们能够知道,若是没有停止请求-响应循环,会执行全部的中间件;若是不是最后一个停止的中间件,必须执行next()。 咱们看一下express中间件的模型: 从上面的编程模型,咱们能够看出每个中间件就是插入到请求开始和响应结束中间的东西,这也是中间件名字的由来。session
中间件的最大优势就是模块化,每个中间件是一个独立的函数,实现一个特定的功能,而后经过app.sue将这个函数整合起来。抽离出来就是一个单独的模块。好比,咱们要实现一个功能,获取当前请求的url,那么咱们就能够将这个功能,封装成一个中间件。app
//实现获取url的模块,封装成中间件
const fn = (req, res, next) => { const url = req.url; console.log(url); next(); }; app.use(fn); const port = 3000; app.listen(port,() => { console.log(`应用运行在${port}端口`); }) 复制代码
Express中的中间件,根据做用范围大体能够分为应用级中间件和路由级中间件。事实上中间件的分类并无特别明显的区别。框架
应用级中间件主要是经过app.use(),以及它的一些语法糖中应用的中间件。编辑器
const express = require('express');
const app = express(); // 应用级中间件 app.use((req, res, next) => { const url = req.url; next(); }); // 应用级中间件 app.get('/user',(req,res) => { res.send('应用级中间件'); }) const port = 3000; app.listen(port,() => { console.log(`应用运行在${port}端口`); }) 复制代码
上面的代码中,咱们调用了两个中间件,一个是app.use()直接调用的中间件,另一个是app.use的语法糖app.get()执行的中间件,为何说app.get()是app.use()的语法糖,由于app.get可以实现的方法使用app.use都可以实现。咱们试着将上面app.get的中间件用app.use实现。模块化
app.use((req,res,next) => {
console.log(req.path) if(req.method === "GET" && req.path == '/users'){ res.send('使用app.use实现的中间件'); } }) // 功能上等价于 app.get('/user',(req,res) => { res.send('应用级中间件'); }) 复制代码
const express = require('express');
const app = express(); // 定义一个路由 const user = express.Router(); user.use("/", (req, res) => { res.send("user.use"); }); user.get("/user", (req, res) => { res.send("路由级别中间件"); }); user.get("/blog", (req, res) => { res.send("路由级中间件"); }); const port = 3000; app.listen(port,() => { console.log(`应用运行在${port}端口`); }) 复制代码
路由级中间件是经过使用express内置的Router生成一个小应用,这个应用跟app具备相同的功能,只不过它主要的功能是用来实现路由。 这种路由功能能够很方便地帮助咱们进行路由模块化开发。咱们能够将相关的路由弄到同一个模块当中。好比上面的user和blog路由能够抽成 user子路由模块和blog子路由模块。示例以下:
const express = require('express');
const app = express(); const user = require('./routes/user.js'); const blog = require('./routes/blog.js'); // 抽离子路由 app.use('/user',user); app.use('/blog',blog); const port = 3000; app.listen(port,() => { console.log(`应用运行在${port}端口`); }) 复制代码
以后只要路径是/user的都会交给user这个子路由去处理。子路由user.js中代码以下:
const express = require('express');
const router = express.Router(); router.get('/',(req,res) => { res.send('这里是user路由'); }) module.exports = router; 复制代码
在上面的讲述中,咱们知道中间件是一个函数,它是嵌入到一个请求和响应循环中的一个独立的功能块。函数的参数是req,res,next。所以编写一个中间件就显得很简单了。至关于编写一个函数,参数为req,res,next。
// 定义
const getUrl = (req,res,next) => { console.log(req.url); next(); } // 使用 app.use(getUrl) 复制代码
从上面的代码咱们能够看出,开发一个中间件就是编写一个函数。可是有时候为了可以配置一些参数,咱们一般是返回一个函数,好比这样:
const getUrl2 = (options) => {
return () => { console.log(`${options.pre}+req.url`); next(); } } app.use(getUrl2({pre:'url:'})) 复制代码
这样的话,就须要app.use(getUrl())这样执行了。这就是为何咱们常常看到app.use(bodyParser())这样的使用方法了。实际上返回的仍是一个函数,只是为了方便配置参数罢了。
最后咱们例举一些express经常使用的中间件
到目前为止,咱们详细介绍了Express的核心中间件,从中间件的详细理解到中间件的分类和使用,以及中间件的编写,最后列举了一些常见的中间件。经过本文咱们基本上就可以掌握Express的核心功能了,其余的Express的API都是中间件的使用罢了。
本文使用 mdnice 排版