express中间件

Express中,中间件是一个功能强大的概念,它允许你在请求被处理之前或之后执行代码。中间件函数可以访问请求对象 (req)、响应对象 (res),以及应用中的下一个中间件函数 (next)

假如我想在Express项目的每一次请求中,添加一个打印日志的功能,如果在所有的请求中console打印肯定不现实,不如抽出一个logs方法,然后在每次请求时候去调用:

const express = require('express')

const app = express()
const PORT = process.env.PORT || 3000


function logs(req, res) {
    console.log(`${req.method},${req.url}, ${new Date().toLocaleString()}`)
}

app.get('/', (req, res) => {
    logs(req, res)
    res.send('Hello World')
})

app.get('/login', (req, res) => {
    logs(req, res)
    res.send('login')
})

app.get('/register', (req, res) => {
    logs(req, res)
    res.send('register')
})

app.listen(PORT, () => {
    console.log(`Server is running on http://localhost:${PORT}`)
})

但是这样的方式还是有些不方便;需要在每次请求中都去调用logs方法,不仅不方便,而且还会有很多重复的代码。

所以,我们可以把logs方法作为一个中间件,然后在app.use()中使用它:

const express = require('express')
const app = express()
const PORT = process.env.PORT || 3000


function logs(req, res, next) {
    console.log(`${req.method},${req.url}, ${new Date().toLocaleString()}`)
    // 下一步操作
    next()
}

app.use(logs)

app.get('/', (req, res) => {
    res.send('Hello World')
})

app.get('/login', (req, res) => {
    res.send('login')
})

app.get('/register', (req, res) => {
    res.send('register')
})

app.listen(PORT, () => {
    console.log(`Server is running on http://localhost:${PORT}`)
})

Express 中,中间件可以写在 app.get 方法之前、之后或之间,具体取决于中间件的作用和你希望它在请求处理过程中的执行顺序。

中间件分类

在 Express 框架中,中间件(Middleware)是一个函数,在请求被发送到路由处理程序之前,可以对请求进行预处理、处理请求、或者对响应进行后处理。Express 中间件可以分为以下几种类型:

  1. 应用级中间件:应用级中间件绑定到 Express 的应用实例(app 对象)上,它们通过 app.use() 方法使用。这种中间件能够处理应用中的所有请求,对请求和响应进行修改,执行一些公共任务,比如日志记录、身份验证等。

  2. 路由级中间件:路由级中间件与应用级中间件类似,但它绑定到特定的路由上,只能处理特定路由的请求。通过 app.use() 或者 router.use() 方法使用,它能够为特定路由的请求执行特定的任务,比如验证用户权限、数据验证等。

  3. 错误处理中间件:错误处理中间件专门用于处理请求过程中发生的错误。当一个中间件通过 next(err) 方法传递错误时,错误处理中间件将被调用。它接收四个参数(err, req, res, next),并负责处理错误、生成错误响应以及记录错误日志等操作。

  4. 内置中间件:Express 框架内置了一些常用的中间件,比如 express.static(用于提供静态文件服务)、express.json(用于解析 JSON 请求体)、express.urlencoded(用于解析 URL 编码的请求体)等。这些内置中间件可以通过简单的配置来使用,提供了一些基本的功能,可以快速开发 Web 应用。

  5. 第三方中间件:除了内置中间件之外,Express 还支持第三方中间件,开发者可以通过 npm 安装并使用第三方中间件来扩展 Express 的功能。第三方中间件可以实现各种功能,比如身份验证、日志记录、性能监控等,丰富了 Express 生态系统,为开发者提供了更多选择。

应用级中间件

在 Express 中,应用级中间件直接与 Express 应用的实例相关,对整个应用生效。它们通常通过 app.use 或者对特定的路由使用 app.METHOD(例如 app.get)来注册。以下是一个简单的例子:

const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;

// 应用级中间件
app.use((req, res, next) => {
    console.log('This middleware is executed for every request to the app.');
    next(); // 调用 next() 将控制权传递给下一个中间件或路由处理程序
});

app.get('/user', (req, res, next) => {
    console.log('Handling user request...');
    next();
}, (req, res) => {
    res.send('User route response');
});

app.listen(PORT, () => {
    console.log(`Server is running on http://localhost:${PORT}`);
});

在这个例子中,app.use 注册了一个应用级中间件,它在每个请求到达应用时都会执行。对于 /user 路由,我们通过 app.get 注册了两个中间件,其中第一个中间件在 /user 请求前执行,记录了请求的处理过程。

应用级中间件可以用于执行一些全局的任务,比如记录日志、身份验证、处理请求等。由于它们在整个应用范围内生效,因此适用于需要在每个请求中执行的任务。

路由级中间件

在 Express 中,路由级中间件是与特定路由或路由组相关的中间件。它们通过 express.Router 创建,并通过 app.use 或者 app.METHOD(例如 app.get)来注册。以下是一个简单的应用:

新建/router/index.js/router/video.js文件,内容如下:

  • /router/index.js:
const express = require('express')
const router = express.Router()

router.get('/user', (req, res) => {
    console.log(req.method)
    res.send('success')
})

router.get('/login', (req, res) => {
    console.log(req.method)
    res.send('login success')
})

module.exports = router
  • /router/video.js:
const express = require('express')
const router = express.Router()

router.get('/list', (req, res) => {
    console.log(req.method)
    res.send('list success')
})

router.get('/detail', (req, res) => {
    console.log(req.method)
    res.send('detail success')
})

module.exports = router

然后在app.js中引入并注册,同时注册时候给他们前面分别添加两个前缀/api/video:

const express = require('express')
const router = require('./router/index')
const videoRouter = require('./router/video')

const app = express()
app.use('/api', router)
app.use('/video', videoRouter)

const PORT = process.env.PORT || 3000

app.listen(PORT, () => {
    console.log(`Server is running on http://localhost:${PORT}`)
})

错误处理中间件

在 Express 中,错误处理中间件是指处理错误的中间件,它可以处理请求和响应,并对它们进行修改。

错误处理中间件可以捕获应用中的错误,并将其发送给客户端。以下是一个简单的例子:

const express = require('express')
const router = require('./router/index')
const videoRouter = require('./router/video')

const app = express()
app.use('/api', router)
app.use('/video', videoRouter)

//捕获路由错误
app.use((req, res, next) => {
    res.status(404).send({
        message: 'Not Found'
    })
})

//捕获错误
app.use((err, req, res, next) => {
    res.status(500).send({
        message: 'server error'
    })
})

const PORT = process.env.PORT || 3000

app.listen(PORT, () => {
    console.log(`Server is running on http://localhost:${PORT}`)
})

内置中间件

Express 内置的中间 件主要用于处理常见的任务,如解析请求体、处理静态文件等。以下是一些常见的 Express 内置中间件:

1. express.json()

用于解析传入请求的 JSON 数据,并将其放置在 req.body 中。

const express = require('express');
const app = express();

app.use(express.json());

app.post('/api/data', (req, res) => {
  console.log(req.body); // 访问 JSON 数据
  res.send('Data received successfully.');
});

app.listen(3000, () => {
  console.log('Server is running on port 3000');
});

2. express.urlencoded()

用于解析传入请求的 URL 编码数据(通常来自表单),并将其放置在 req.body 中。

const express = require('express');
const app = express();

app.use(express.urlencoded({ extended: true }));

app.post('/api/form', (req, res) => {
  console.log(req.body); // 访问表单数据
  res.send('Form data received successfully.');
});

app.listen(3000, () => {
  console.log('Server is running on port 3000');
});

3. express.static()

用于提供静态文件,例如 HTML、CSS、JavaScript 等。

const express = require('express');
const app = express();

app.use(express.static('public'));

app.get('/', (req, res) => {
  res.sendFile(__dirname + '/public/index.html');
});

app.listen(3000, () => {
  console.log('Server is running on port 3000');
});

4. express.Router()

虽然不是直接中间件,但用于创建模块化、可挂载的路由处理程序。

// 在 router.js 文件中
const express = require('express');
const router = express.Router();

router.get('/', (req, res) => {
  res.send('Router response');
});

module.exports = router;

// 在主应用中
const express = require('express');
const app = express();
const router = require('./router');

app.use('/api', router);

app.listen(3000, () => {
  console.log('Server is running on port 3000');
});

这些内置中间件使得 Express 应用更易于开发,并提供了一些基本的功能,可以通过简单的配置即可使用。

第三方中间件

Express 社区提供了许多第三方中间件,这些中间件可以用于增强 Express 应用的功能。以下是一些常用的第三方中间件:

  • body-parser

用于解析请求体,支持 JSON、URL 编码和多部分数据。

npm install body-parser

使用方法:

const express = require('express');
const bodyParser = require('body-parser');
const app = express();

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

app.post('/api/data', (req, res) => {
  console.log(req.body); // 访问 JSON 或 URL 编码数据
  res.send('Data received successfully.');
});

app.listen(3000, () => {
  console.log('Server is running on port 3000');
});
  • helmet

提供了一系列 HTTP 头的设置,帮助提高 Express 应用的安全性。

npm install helmet

使用方法:

const express = require('express');
const helmet = require('helmet');
const app = express();

app.use(helmet());

app.get('/', (req, res) => {
  res.send('Hello, World!');
});

app.listen(3000, () => {
  console.log('Server is running on port 3000');
});
  • compression

用于压缩响应体,减小传输数据的大小。

npm install compression

使用方法:

const express = require('express');
const compression = require('compression');
const app = express();

app.use(compression());

app.get('/', (req, res) => {
  res.send('Compressed response.');
});

app.listen(3000, () => {
  console.log('Server is running on port 3000');
});
  • morgan

用于记录 HTTP 请求的日志。

npm install morgan

使用方法:

const express = require('express');
const morgan = require('morgan');
const app = express();

app.use(morgan('combined'));

app.get('/', (req, res) => {
  res.send('Hello, World!');
});

app.listen(3000, () => {
  console.log('Server is running on port 3000');
});

这只是一小部分可用的第三方中间件。根据应用需求,你可以选择并安装适合的中间件来增强 Express 应用的功能。

Express路由

all方法用于匹配所有 HTTP 动词(GET、POST、PUT、DELETE 和 OPTIONS)。

const express = require('express')
const app = express()
const PORT = process.env.PORT || 3000

app.all('/*', (req, res) => {
    res.send('all')
})

app.listen(PORT, () => {
    console.log(`Server is running on http://localhost:${PORT}`)
})

路由路径

路由路径也可以是字符串模式。可用部分正则表达式语法来定义端点的模式。以下是所涉及的正则表达式(注意,连字符( -)和点(.)在字符串路径中解释为字面量,不能做为正则表达式)

  • 路径中?用于匹配路径中包含一个或多个字符的路径。
const express = require('express')
const app = express()


app.get('/us?er', (req, res) => {
    res.send(`${req.url}`)
})

const PORT = process.env.PORT || 3000

app.listen(PORT, () => {
    console.log(`Server is running on http://localhost:${PORT}`)
})

请求下面接口都可以请求通:

http://localhost:3000/uer
http://localhost:3000/user
  • 路径中+用于匹配路径中包含多个字符的路径。
const express = require('express')
const app = express()


app.get('/us+er', (req, res) => {
    res.send(`${req.url}`)
})

const PORT = process.env.PORT || 3000

app.listen()

只要请求下面的路径都可以请求通

http://localhost:3000/user
http://localhost:3000/ussser
http://localhost:3000/usssser
  • 路径中*可以替换为任意字符串
const express = require('express')
const app = express()


app.get('/us+er', (req, res) => {
    res.send(`${req.url}`)
})

const PORT = process.env.PORT || 3000

app.listen()

只要请求下面的路径都可以请求通

http://localhost:3000/ussssser
http://localhost:3000/usABCer

路由参数

路由参数是指在路径中使用冒号(:)来定义的部分。

const express = require('express')
const app = express()


app.get('/user/:id', (req, res) => {
    console.log(req.params)
    res.send(`${req.url}`)
})

const PORT = process.env.PORT || 3000

app.listen()

请求下面接口都可以请求通:

http://localhost:3000/user/123 // 输出 { id: '123' }
http://localhost:3000/user/abc // 输出 { id: 'abc' }

也可以设置多个路由参数

const express = require('express')
const app = express()


app.get('/user/:id/:name', (req, res) => {
    console.log(req.params)
    res.send(`${req.url}`)
})

const PORT = process.env.PORT || 3000

app.listen()

请求下面接口都可以请求通:

http://localhost:3000/user/123/abc // 输出 { id: '123', name: 'abc' }
http://localhost:3000/user/abc/123 // 输出 { id: 'abc', name: '123' }

链式路由

链式路由是指在一个路由中定义另一个路由,并在该路由中定义其它的路由。

const express = require('express')
const app = express()

app.get('/user', (req, res) => {
    res.send(`${req.url}`)
}).post('/login', (req, res) => {
    res.send(`${req.url}`)
})

const PORT = process.env.PORT || 3000
app.listen()

响应方法

响应方法用于向客户端发送响应,同时也支持链式调用。

res.send()方法用于发送字符串。

const express = require('express')
const app = express()

app.get('/user', (req, res) => {
    res.send('Hello, World!')
})

const PORT = process.env.PORT || 3000
app.listen()

res.download()方法用于下载文件。

const express = require('express')
const app = express()

app.get('/user', (req, res) => {
    res.download('path/to/file')
})

const PORT = process.env.PORT || 3000
app.listen()

res.json()方法用于发送 JSON 数据。

const express = require('express')
const app = express()

app.get('/user', (req, res) => {
    res.json({
        name: 'John Doe',
        age: 30
    })
})

const PORT = process.env.PORT || 3000
app.listen()

res.end()方法用于结束响应。

const express = require('express')
const app = express()

app.get('/user', (req, res) => {
    res.end('Hello, World!')
})

const PORT = process.env.PORT || 3000
app.listen()

res.redirect()方法用于重定向到另一个 URL。

const express = require('express')
const app = express()

app.get('/user', (req, res) => {
    res.redirect('/login')
})

const PORT = process.env.PORT || 3000
app.listen()

res.render()方法用于渲染模板。

const express = require('express')
const app = express()

app.get('/user', (req, res) => {
    res.render('index', {
        title: 'Hello, World!'
    })
})

const PORT = process.env.PORT || 3000
app.listen()

res.sendStatus()方法用于设置响应状态码。

const express = require('express')
const app = express()

app.get('/user', (req, res) => {
    res.sendStatus(200)
})

const PORT = process.env.PORT || 3000
app.listen()