一、前言

基于 Node.js 平台,快速、开放、极简的 Web 开发框架 。

1. 框架的作用

框架可以帮助省略掉一些基本的相同底层代码的反复书写,只需调用框架的方法就可以实现你想要的功能。

回顾一下 Node - 002 - 搭建一个Web服务Node - 003 - Web服务升级 这两篇文章。写原生实现总是非常繁琐且费时的。

2. Node 相关的框架

  1. express
  2. koa
  3. egg
  4. thinkjs
  5. adonisjs
  6. nestjs

    …..

二、安装

  1. $ npm install express

三、Hello World

  1. const express = require('express');
  2. const app = express();
  3. app.get('/', function(req, res) {
  4. res.send('hello world');
  5. });
  6. app.listen(3000, () => {
  7. console.log('服务启动成功! http://localhost:3000');
  8. });

四、路由

路由是指确定应用程序如何响应客户端(浏览器)对特定端点的请求,该特定端点是URL(或路径)和特定的HTTP请求方式(GET、POST等)。 每个路由可以具有一个或多个处理程序函数,这些函数在路由匹配时执行。

1. 路由的基本语法

  1. app.METHOD(PATH, [...HANDLER])
  1. app 是 express 的实例。
  2. METHOD 是小写的 HTTP 请求方式(GET、POST、PUT、PATCH、DELETE等)。
  3. PATH 是请求路径。(以 / 开头)
  4. HANDLER 是当路由匹配时执行的函数。
  1. // 1. 处理 GET / 请求时
  2. app.get('/', (req, res) => {
  3. res.send('GET / 响应')
  4. })
  5. // 2. 处理 POST / 请求时
  6. app.post('/', (req, res) => {
  7. res.send('POST / 响应')
  8. })
  9. // 3. 处理 GET /users 请求时
  10. app.get('/users', (req, res) => {
  11. res.send('GET /users 响应')
  12. })
  13. // 4. 处理 DELETE /users 请求时
  14. app.delete('/users', (req, res) => {
  15. res.send('DELETE /users 响应')
  16. })

2. 请求参数的获取

  1. query 参数(?号传参)
  1. Route Path : /hello
  2. Request URL : http://localhost:3000/hello?name=zhangsan&age=18
  3. req.query : { name: 'zhangsan', age: '18' }
  1. body 参数(请求体传参)

注意:req.body 需要设置中间件

  1. const express = require('express')
  2. const bodyParser = require('body-parser')
  3. const app = express()
  4. /**
  5. * 处理 请求体参数的 解析
  6. * Express 4.16.0 之前使用 bodyParser 第三方模块
  7. */
  8. app.use(bodyParser.json())
  9. app.use(bodyParser.urlencoded({ extended: true }))
  10. /**
  11. * 处理 请求体参数的 解析。
  12. * Express 4.16.0 起 提供了内置的 express.json() 和 express.urlencoded()
  13. * 当然他们内部还是基于 bodyParser 的
  14. */
  15. // app.use(express.json())
  16. // app.use(express.urlencoded({ extended: true }))
  17. app.post('/login', (req, res) => {
  18. console.log(req.body)
  19. res.send('POST /login 响应')
  20. })
  21. app.listen(3000, () => {
  22. console.log('服务启动成功')
  23. })
  1. Route Path : /login
  2. Request URL : http://localhost:3000/login
  3. Request BODY: username=lisi&password=123456或者 { username: 'lisi', password: '123456' }
  4. req.body : { username: 'lisi', password: '123456' }
  1. params 参数(动态路径传参)
  1. Route Path : /users/:userId/books/:bookId
  2. Request URL : http://localhost:3000/users/34/books/8989
  3. req.params : { userId: '34', bookId: '8989' }

3. 路由处理函数

路由处理函数是一种类似于 中间件函数 的方法。一个路由可以同时设置多个路由处理函数。

接收三个参数:

  1. req:request 请求对象
  2. res:response 响应对象
  3. next:调用它执行下一个匹配的路由处理函数

路由处理函数的多种组合形式:

  1. // 1. 单个回调函数的形式
  2. app.get('/example/a', (req, res) => {
  3. res.send('GET /example/a 响应')
  4. })
  5. // 2. 多个回调函数以参数列表形式,注意 next 回调
  6. app.get('/example/b', (req, res, next) => {
  7. console.log('1')
  8. next()
  9. }, (req, res, next) => {
  10. console.log('2')
  11. res.send('GET /example/b 响应')
  12. })
  13. // 3. 多个回调函数以数组形式,注意 next 回调
  14. app.get('/example/c', [
  15. (req, res, next) => {
  16. console.log('CB-1')
  17. next();
  18. },
  19. (req, res, next) => {
  20. console.log('CB-2')
  21. next();
  22. },
  23. (req, res, next) => {
  24. res.send('GET /example/c 响应')
  25. }
  26. ])
  27. // 4. 组合形态
  28. app.get('/example/d', [
  29. (req, res, next) => {
  30. console.log('CB-1')
  31. next();
  32. },
  33. (req, res, next) => {
  34. console.log('CB-2')
  35. next();
  36. },
  37. ], (req, res, next) => {
  38. res.send('GET /example/d 响应')
  39. })

4. express.Router

使用 express.Router 类来创建模块化的,可安装的路由处理程序。一个 Router 实例是一个完整的中间件和路由系统。

思考如下代码:
当项目做大做强时,index.js 文件将异常庞大。不利于后续的项目维护。

  1. // index.js
  2. const express = require('express')
  3. const app = express()
  4. app.get('/', (req, res) => { res.send('GET / 响应') })
  5. app.get('/posts', (req, res) => { res.send('GET /posts 响应') })
  6. app.post('/posts', (req, res) => { res.send('POST /posts 响应') })
  7. app.get('/posts/create', (req, res) => { res.send('GET /posts/create 响应') })
  8. app.get('/posts/:id', (req, res) => { res.send('GET /posts/xxid 响应') })
  9. app.put('/posts/:id', (req, res) => { res.send('PUT /posts/xxid 响应') })
  10. app.get('/posts/:id/edit', (req, res) => { res.send('GET /posts/xxid/edit 响应') })
  11. app.delete('/posts/:id', (req, res) => { res.send('DELETE /posts/xxid 响应') })
  12. app.get('/books', (req, res) => { res.send('GET /books 响应') })
  13. app.post('/books', (req, res) => { res.send('POST /books 响应') })
  14. app.get('/books/create', (req, res) => { res.send('GET /books/create 响应') })
  15. app.get('/books/:id', (req, res) => { res.send('GET /books/xxid 响应') })
  16. app.put('/books/:id', (req, res) => { res.send('PUT /books/xxid 响应') })
  17. app.get('/books/:id/edit', (req, res) => { res.send('GET /books/xxid/edit 响应') })
  18. app.delete('/books/:id', (req, res) => { res.send('DELETE /books/xxid 响应') })
  19. app.listen(3000)

这时就可以将相同类别的路由处理代码抽离到单独的文件中,最后在主程序(这里指 index.js)中加载。比如:

  1. 抽离 posts 相关的存放到 routes/posts.js 文件中
  1. // routes/posts.js
  2. const express = require('express')
  3. const router = express.Router()
  4. router.get('/', (req, res) => { res.send('GET /posts 响应') })
  5. router.post('/', (req, res) => { res.send('POST /posts 响应') })
  6. router.get('/create', (req, res) => { res.send('GET /posts/create 响应') })
  7. router.get('/:id', (req, res) => { res.send('GET /posts/xxid 响应') })
  8. router.put('/:id', (req, res) => { res.send('PUT /posts/xxid 响应') })
  9. router.get('/:id/edit', (req, res) => { res.send('GET /posts/xxid/edit 响应') })
  10. router.delete('/:id', (req, res) => { res.send('DELETE /posts/xxid 响应') })
  11. // 不要忘了暴露出去
  12. module.exports = router
  1. 抽离 books 相关的存放到 routes/books.js 文件中
  1. // routes/books.js
  2. const express = require('express')
  3. const router = express.Router()
  4. router.get('/', (req, res) => { res.send('GET /books 响应') })
  5. router.post('/', (req, res) => { res.send('POST /books 响应') })
  6. router.get('/create', (req, res) => { res.send('GET /books/create 响应') })
  7. router.get('/:id', (req, res) => { res.send('GET /books/xxid 响应') })
  8. router.put('/:id', (req, res) => { res.send('PUT /books/xxid 响应') })
  9. router.get('/:id/edit', (req, res) => { res.send('GET /books/xxid/edit 响应') })
  10. router.delete('/:id', (req, res) => { res.send('DELETE /books/xxid 响应') })
  11. // 不要忘了暴露出去
  12. module.exports = router
  1. 在主程序中(index.js)中引入并使用
  1. // index.js
  2. const express = require('express')
  3. // 引入
  4. const postsRouter = require('./routes/posts.js')
  5. const booksRouter = require('./routes/books.js')
  6. const app = express()
  7. app.get('/', (req, res) => { res.send('GET / 响应') })
  8. // 使用
  9. app.use('/posts', postsRouter)
  10. app.use('/books', booksRouter)
  11. app.listen(3000)