🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] # express简介 Express 对原生 node.js 做了进一步的封装,使用起来更为方便,代码更为简洁。 Express 是一个自身功能极简,完全由路由和中间件构成的一个 web 开发框架。从本质上来说,一个 Express 应用就是在调用各种中间件。 # Hello world示例 ```js const express = require('express') const app = express() app.get('/', function(req, res) { res.send('hello world') }) app.listen(3000) ``` 一般让 express() 方法返回一个名为 app 的对象,这个对象预设了一系列的方法 API 中分为 Properties,Events,Methods app.listen 方法实际上是创建了一个 HTTP 服务,如下 ```js app.listen = function () { let server = http.createServer(this) return server.listen.apply(server, arguments) } ``` 其可以接受一个回调函数: ```javaScript // 回调函数打印监听的地址和端口号 const server = app.listen(3000, () => { const host = server.address().address const port = server.address().port console.log('server is listening at http://%s:%s', host, port) }) ``` # middleware(中间件) 中间件的行为类似 Java 中过滤器的工作原理,就是在进入具体的业务处理之前,先让过滤器处理,比如对于每个请求我们一般都要解析 cookie,querystring 什么的,那么就设计对应的中间件处理完成后存储在上下文中(req 和 res,Koa2 合并为一个 context) ![](https://box.kancloud.cn/22d3c8400e5948030aa16412085e73c0_901x501.png) ```js // 模拟中间件的实现 const http = require('http') const slice = Array.prototype.slice class LikeExpress { constructor() { // 存放中间件的列表 this.routes = { all: [], // 存放 app.use 注册的中间件 get: [], // 存放 app.get 注册的中间件 post: [] } } // 内部实现注册的方法 register(path) { const info = {} if (typeof path === 'string') { // 字符串 - 路由 info.path = path // 从第二个参数开始,转换为数组,存入stack info.stack = slice.call(arguments, 1) // 取出剩余的参数 } else { // 没有显式地传入路由则默认是根路由 info.path = '/' // 省略第一个参数 -> 根目录 // 从第一个参数开始,转换为数组,存入stack info.stack = slice.call(arguments, 0) } // { path: '', stack: [middleware, ...] } return info } use() { const info = this.register.apply(this, arguments) // 把当前函数的所有参数传入 this.routes.all.push(info) } get() { const info = this.register.apply(this, arguments) // 把当前函数的所有参数传入 this.routes.get.push(info) } post() { const info = this.register.apply(this, arguments) // 把当前函数的所有参数传入 this.routes.post.push(info) } match(method, url) { let stack = [] // resultList if (url === '/favicon.ico') { // 小图标无视 return stack } // 获取 routes let curRoutes = [] curRoutes = curRoutes.concat(this.routes.all) curRoutes = curRoutes.concat(this.routes[method]) curRoutes.forEach(routeInfo => { if (url.indexOf(routeInfo.path === 0)) { // url === '/api/get-cookie' 且 routeInfo.path === '/' // url === '/api/get-cookie' 且 routeInfo.path === '/api' // url === '/api/get-cookie' 且 routeInfo.path === '/api/get-cookie' stack = stack.concat(routeInfo.stack) } }) return stack } // 核心的 next 机制 handle(req, res, stack) { const next = () => { // 拿到第一个匹配的中间件 const middleware = stack.shift() if (middleware) { // 执行中间件函数 middleware(req, res, next) } } next() } callback() { return (req, res) => { // 自己定义 res.json 方法 res.json = data => { res.setHeader('Content-Type', 'application/json') res.end( JSON.stringify(data) ) } // 获取 url 和 method :通过这两个来获得需要经过的中间件 const url = req.url const method = req.method.toLowerCase() // match 函数匹配可用的中间件列表 const resultList = this.match(url, method) this.handle(req, res, resultList) } } listen(...args) { const server = http.createServer(this.callback) server.listen(...args) } } // 工厂函数 module.exports = () => { return new LikeExpress() } ``` # 常用的中间件及其功能 ## 解析 request 对象 - `req.body`:使用 body-parser 中间件帮我们处理后可以直接通过 req.body 获取 POST请求的实体数据(axios 的 data 选项) ```js var app = require('express')(); var bodyParser = require('body-parser'); var multer = require('multer'); // v1.0.5 var upload = multer(); // for parsing multipart/form-data app.use(bodyParser.json()); // for parsing application/json app.use(bodyParser.urlencoded({ extended: true })); // for parsing application/x-www-form-urlencoded app.post('/profile', upload.array(), function (req, res, next) { console.log(req.body); res.json(req.body); }); ``` 现在 body-parser 已经成为内置中间件了,我们使用 express 脚手架构建([https://www.jianshu.com/p/a77b806b0d14](https://www.jianshu.com/p/a77b806b0d14))的项目可以看到如下两行(注释我写的): ```js // built-in middleware // 基于 body-parser POST 请求有 4 种数据格式,因此需要与下面一个中间件结合使用 app.use(express.json()) // built-in middleware // Content-Type: x-www-... 用表单提交时的 POST 数据格式 解析结果会存入 req.body 和 JSON 格式数据一样 app.use(express.urlencoded({ extended: false })) ``` - `req.cookies`:使用了 cookie-parser 中间件后可以直接通过 req.cookies 来获取 cookies 信息 ```js // Cookie: name=tj req.cookies.name // => "tj" ``` - `req.query`:可直接通过 req.query 获取 querystring,如果没有返回空对象 {} ```js // GET /search?q=tobi+ferret req.query.q // => "tobi ferret" // GET /shoes?order=desc&shoe[color]=blue&shoe[type]=converse req.query.order // => "desc" req.query.shoe.color // => "blue" req.query.shoe.type // => "converse" ``` - `req.path`:Contains the path part of the request URL. ```js // example.com/users?sort=desc req.path // => "/users" ``` - `req.hostname`:Contains the hostname derived from the`Host`HTTP header. ```js // Host: "example.com:3000" req.hostname // => "example.com" ```