Express从入门到精通

一. Express简介

Express 是一个极简而灵活的 node.js Web应用框架, 提供了一系列强大特性帮助你创建各种 Web 应用,和丰富的 HTTP 工具。Express中文官网

为什么说Express是一个极简的框架

Express的核心只有两个部分

  • 路由

  • 中间件

Express提供了基本的路由处理和中间件功能, 几乎所有功能的实现都由一个个独立的中间件完成

1 路由

路由可以认为是一种找到数据的路径, 或者说是URL+处理函数

  • 通过URL来区分不同的资源(页面或数据)

  • 通过处理函数来返回资源(页面或数据)

前端通过URL请求数据.

后端通过不同的路由, 调用对应的方法, 返回指定的数据

2 中间件

顾名思义, 中间件就是在什么的中间

在请求和响应中间的处理程序

有时候从请求到响应的业务比较复杂, 将这些复杂的业务拆开成一个个功能独立的函数, 就是中间件

对于处理请求来说,在响应发出之前,可以在请求和响应之间做一些操作,并且可以将这个处理结果传递给下一个函数继续处理

express 中间件函数,帮助拆解主程序的业务逻辑,
并且每一个的中间件函数处理的结果都会传递给下一个中间件函数。
就好比工厂里流水线工人清洗一个箱子:
第一个人清洗侧面,第二个人清洗底面,第三个人清洗顶面,。。。
这条流水线结束后,箱子也就清洗干净了
各做各的,不相互影响,又彼此协作

二. 安装及使用

express也是一个node的包, 可以npm来安装

1 安装

npm i express

2 使用

:::info

步骤

  1. 导入express包

  1. 实例化对象

  1. 编写路由(中间件)

  1. 监听端口

示例
// 1. 引入express包
const express = require('express')
// 2. 实例化对象
const app = express()
// 3. 编写路由
app.get('/', function(req, res) {
  res.send('hello world')
})
// 4. 监听端口
app.listen(3000)

三. 路由

路由模块由三部分组成

  • 请求方式

  • URL

  • 处理函数

路由模块主要方法是 app.METHOD

练习
// 需求:
//    1. 当GET请求/或者/index.html时, 返回 '首页'
//    2. 当GET请求/list.html时, 返回 '列表页'
//    3. 当GET请求/detail.html时, 返回 '详情页'
//    4. 其它情况 返回, '404 Not Found'
示例
// 1. 导入express包
const express = require('express')
// 2. 实例化app对象
const app = express()
// 3. 编写路由(根据不同的path, 返回不同的内容)
app.get('/', function (req, res) {
  // req: request(请求对象)
  // res: response(响应对象)
  res.send('首页')
})
app.get('/index.html', function (req, res) {
  res.send('首页')
})
app.get('/list.html', function (req, res) {
  res.send('列表页')
})
app.get('/detail.html', function (req, res) {
  res.send('详情页')
})
app.get('*', function (req, res) {
  res.send('404 Not Found')
})
// 4. 监听端口
app.listen(3000, function () {
  console.log('server is running on http://localhost:3000')
})

1 请求方式

请求方式就是HTTP协议的请求方式, 常见的有

  • get: 对应app.get()--查询

  • post: 对应app.post()--创建(新增)

  • put: 对应app.put()--修改

  • delete: 对应app.delete()--删除

2 URL

URL的写法

// 第一种, 不带参数
app.get('/users', function(req, res) {
  res.send('hello world')
})
// 第二种, 带参数
app.get('/users/:id', function(req, res) {
  res.send('hello world')
})
// 第三种, 正则表达式, 以html结尾
// 请求index.html abc.html
app.get(/.html$/, function(req, res) {
  res.send('hello world')
})

3 处理函数

语法
function(req, res, next) {
  // todo
}

在处理函数中, 有两个形参

  • req(请求对象)

  • res(响应对象)

1) 请求对象

请求对象包含了一次请求中的所有数据(http请求头, 请求参数...)

示例
app.get('/', function (req, res) {
  console.log(req)
})

2) 获取请求参数

常见的请求

  • GET请求: 通过URL

  • URL的path部分, eg /users/1

  • URL的queryString部分, eg /users?page=1&size=5

  • POST请求: 通过body

语法
// 对于GET请求的path传参
req.params
// 对于GET请求的queryString传参
req.query
示例: get请求
// 1.导入express包
const express = require('express')
// 2.实例化app对象
const app = express()
// 3.编写路由

/**
 * 获取用户id=1的用户信息
 * GET /users/:id
 * eg. GET /users/1 返回{id:1, name: 'xiaoming', age: 20}
 */
app.get('/users/:id', (req, res) => {
  // 请求对象(包含所有http请求信息)
  console.log(req.params)
  // 查询数据库, 得到user对象, 返回
  res.send({
    id: 1,
    name: 'xiaoming',
    age: 20,
  })
})
/**
 * 获取所有用户(限定条件: 第1页,每页显示5条)
 * GET /users
 * 请求参数 urlencoded [?page=1&size=5]queryString
 * 响应数据 [{}, {}, {}, {}, {}]
 */
app.get('/users', (req, res) => {
  // 通过req.query获取GET请求中的queryString的值
  console.log(req.query)
  res.send([
    { id: 1, name: 'xiaoming' },
    { id: 2, name: 'xiaomei' },
    { id: 3, name: 'xiaopang' },
  ])
})
// 4.监听端口
app.listen(3000)
示例: post请求
// 1. 导入express包
const express = require('express')
// 2. 实例化app对象
const app = express()
// 3. 编写路由
/**
 * 创建用户
 * POST /users
 * 请求参数: {name: 'xiaoming', age: 20}
 * 响应: {id: 5, name: 'xiaoming', age: 20}
 */
app.post('/users', (req, res) => {
  // 解析req中的请求体参数
  let postData = ''
  req.on('data', (data) => {
    // 把所有的post的数据拼接
    postData = postData + data
  })
  req.on('end', () => {
    // 当所有的数据接收完成
    // 将postData字符串->JSON格式的对象
    const obj = JSON.parse(postData)
    console.log(obj) // 在后端的控制台中观察(Cmd窗口)
    res.send(obj) // 将数据返回客户端(浏览器Network中观察)
  })
})
// 4. 监听端口
app.listen(3000)

根据id查找对应的数据并返回

编写post.http

POST http://localhost:3000/
Content-Type: application/json

{
	"name": "xiaoming"
}

3) 响应对象

响应对象用于向客户端返回数据, 在处理函数中需要调用以返回数据

常用的有两个

  • res.send(): 返回各种类型

  • res.json(): 返回json格式的数据

作业

实现一个商品模块

const db = [
  {id: 1, name: 'iphone13', price: '5299', number: '10'},
  {id: 2, name: 'MacBookPro M1', price: '11999', number: '20'},
  {id: 3, name: 'iPad Pro', price: '4899', number: '30'}
]

写5个接口

  • 获取所有数据

  • 根据id获取单个数据

  • 新增数据

  • 修改数据

  • 删除数据

四. 中间件

中间件的设计模式符合软件工程中的经典模式 CP 模式

  • Core: 核心

  • Pulgin: 插件

比如

  • 浏览器, 最核心的是浏览器, 可以通过插件扩展功能

  • VSCode, 最核心的是代码编辑, 可能通过安装各种插件实现不同的功能

这里的中间件类似于Plugin的作用. 可以通过使用中间件来实现丰富的功能

1 中间件类型

2 应用级中间件

在Express中, 使用app.use或者app.METHOD注册的中间件叫做应用级中间件

:::info

中间件就是一个函数

:::

app.use('path', function (req, res, next) {
  next()
})
在中间件中需要通过调用next()执行下一个中间件
如果不执行next(), 也没有调用send(). 这次请求将会被挂起

中间件从影响范围上, 又分为全局中间件和局部中间件

1) 全局中间件

一般, 使用app.use 注册全局中间件

全局中间件: 给每个路由规则都添加中间件处理函数
app.use(function (req, res, next) {
  console.log('我是一个全局中间件, 影响所有的路由规则')
})

2) 局部中间件

可以使用app.use或者app.METHOD注册局部中间件

局部中间件: 给某一个特定的路由规则添加中间件处理函数
app.use('/users', function (req, res, next) {
  console.log(`这里是一个局部中间件, 只影响/users路由`)
  next()
})
示例一
// 不写第一个参数, 给所有访问都注册了一个中间件
app.use(function (req, res, next) {
  console.log('Time:', Date.now())
  next()
})
示例二
app.use('/user/:id', function (req, res, next) {
  console.log('Request Type:', req.method)
  next()
})

app.get('/user/:id', function (req, res, next) {
  res.send('USER')
})
示例三

可以同时注册多个中间件函数

app.use('/user/:id', function (req, res, next) {
  console.log('Request Type:', req.method)
  next()
}, function (req, res, next) {
  console.log('Request Params:', req.params.id)
  next()
})

app.get('/user/:id', function (req, res, next) {
  res.send('USER')
})
示例四

app.use除了注册函数做为中间件外, 还可注册一个express.Router()对象

const router = express.Router()
app.use('/user/:id', router)
作业

:::warning

需求

  1. 编写一个中间件: 是一个全局中间件, 记录请求的时间 Date.now()

  1. 编写一个中间件: 是一个局部中间件, 只有当匹配/users路由时执行. 记录请求的方法

  1. 编写一个中间件: 是一个局部中间件, 只有当匹配/users/:id路由时执行. 记录id值

  1. 当匹配/users/:id时执行, 返回 '用户的id是XX'

:::

/**
 * 全局中间件: app.use(全局中间件)
 * 局部中间件: app.use('路由', 局部中间件)
 */
const express = require('express')
const app = express()

// 编写一个中间件: 是一个全局中间件, 记录请求的时间 Date.now()
app.use(function (req, res, next) {
  console.log(`当前时间: ${Date.now()}`)
  next()
})

// 处理 /users 接口
// 编写一个中间件: 是一个局部中间件, 只有当匹配/users路由时执行. 记录请求的方法
app.use('/users', function (req, res, next) {
  console.log(`请求的方法是: ${req.method}`)
  next()
})
app.get('/users', function (req, res) {
  res.send('用户信息...')
})

// 处理 /users/:id 接口
app.use('/users/:id', function (req, res, next) {
  console.log(`请求的id是: ${req.params.id}`)
  next()
})
app.get('/users/:id', function (req, res) {
  res.send(`用户的id是${req.params.id}`)
})

// app.get(
//   '/users/:id',
//   function (req, res, next) {
//     console.log(`请求的id是: ${req.params.id}`)
//     next()
//   },
//   function (req, res) {
//     res.send(`用户的id是${req.params.id}`)
//   }
// )

app.listen(3000)

3 路由级中间件

express.Router()对象也可以注册中间件.

使用router.use或者router.METHOD注册的中间件叫做路由级中间件

var app = express()
var router = express.Router()

router.use(function (req, res, next) {
  console.log('Time:', Date.now())
  next()
})
router.get('/users/', function(req, res) {
  res.send('hello')
})
路由级中间件的应用

当路由很多的时候, 如果全部写在app入口会使用文件过大, 不好维护. 可以把不同的路由拆分成多个模块

const express = require('express')
const userRouter = require('./routes/users.js')

const app = express()
app.use(function (req, res, next) {
  console.log(req.url)
  next()
})
// 加载路由
app.use('/users', userRouter)

app.listen(3000)
const express = require('express')

const router = express.Router()

router.get('/', function(req, res) {
  // 返回所有用户信息
  const data = [
    {id:1, username: 'xiaoming', age: 20},
    {id:2, username: 'xiaomei', age: 18},
    {id:3, username: 'xiaopang', age: 1},
  ]
  res.json(data)
})

router.get('/:id', function(req, res) {
  // 返回所有用户信息
  const user = {id:1, username: 'xiaoming', age: 20}
  res.json(user)
})

module.exports = router

5 小结

路由级中间件的操作步骤

:::warning

步骤

  1. 编写routes/路由文件

  1. 导入express包

  1. 创建路由对象

  1. 编写相关路由

  1. 导出路由对象

  1. 在入口文件中导入路由对象

  1. 注册中间件

:::

五. 数据库操作

1 安装mysql库

参考npm包的官方文档

npm i mysql

2 入门案例

操作数据库, 就是模拟客户端. 向MySQL的服务端发送SQL语句. 基本步骤如下:

  1. 引入mysql包

  1. 创建数据库连接

  1. 连接数据库

  1. 执行SQL查询

  1. 关闭连接

示例
// 1. 引入mysql包
const mysql = require('mysql')

// 2. 创建数据库连接
const con = mysql.createConnection({
  host: 'localhost',
  user: 'root',
  password: '123456',
  database: 'db'
})

// 3. 连接数据库
con.connect()

// 4. 执行查询
con.query('select * from student', function (err, res) {
  if (err) throw err

  console.log(res)
})

// 5. 关闭连接
con.end()

3 集成到express

示例

获取所有的用户

// 1. 导入express包
const express = require('express')
// 2. 实例化app对象
const app = express()
// 3. 路由
/**
 * 获取所有的用户信息
 */
app.get('/users', function (req, res) {
  // 导入mysql的包
  const mysql = require('mysql')
  // 创建连接
  const con = mysql.createConnection({
    host: '127.0.0.1',
    user: 'root',
    password: '123456',
    database: 'user',
  })
  // 连接数据
  con.connect()
  // 执行sql语句
  let sql = 'select * from student'
  con.query(sql, function (err, data) {
    if (err) throw err
    // 返回结果
    res.send(data)
  })
})
// 4. 监听端口
app.listen(3000)
练习

实现 /users/:id根据id获取用户信息

参考答案
/**
 * 根据id获取用户信息
 * GET /users/:id
 */
app.get('/users/:id', function (req, res) {
  // 一. 解析请求数据
  const id = req.params.id
  // 二. 操作数据库
  // 导入mysql的包
  const mysql = require('mysql')
  // 创建连接
  const con = mysql.createConnection({
    host: '127.0.0.1',
    user: 'root',
    password: '123456',
    database: 'user',
  })
  // 连接数据
  con.connect()
  // 执行sql语句
  let sql = `select * from student where id=${id}`
  con.query(sql, function (err, data) {
    if (err) throw err
    // 返回结果
    res.send(data)
  })
})

我们发现操作数据库部分的代码是重复的.

因此, 我们需要对这部分的内容进行封装(模块化编程的思想)

4 封装mysql

在src下创建db/index.js, 编写如下内容

// 封装数据库的操作
// 1. 导入mysql包
const mysql = require('mysql')
// 2. 创建数据库连接
const con = mysql.createConnection({
  host: '127.0.0.1',
  port: 3306,
  user: 'root',
  password: '123456',
  database: 'db',
})
// 3. 连接数据库
con.connect()

// 4. 编写操作
/**
 * 获取所有数据
 * @param {string} sql : 执行的sql语句
 * @return {promise}
 */
function getAll(sql) {
  return new Promise((resolve, reject) => {
    con.query(sql, (err, data) => {
      if (err) reject(err)

      resolve(data)
    })
  })
}

/**
 * 获取所有数据
 * @param {string} sql : 执行的sql语句
 * @return {promise}
 */
function getOne(sql) {
  return new Promise((resolve, reject) => {
    con.query(sql, (err, data) => {
      if (err) reject(err)

      // data是一个数组
      // if (data.length != 0) {
      //   // 查询到了数据
      //   resolve(data[0])
      // } else {
      //   resolve(null)
      // }
      data.length != 0 ? resolve(data[0]) : resolve(null)
    })
  })
}

/**
 * 执行sql语句
 * @param {string} sql : 执行的sql语句
 * @return {promise}
 */
function exec(sql) {
  return new Promise((resolve, reject) => {
    con.query(sql, (err, data) => {
      if (err) reject(err)

      resolve(data)
    })
  })
}

// 暴露3个方法
module.exports = {
  getAll,
  getOne,
  exec,
}

封装后的操作

// 1. 导入express
const express = require('express')
const { getAll, getOne } = require('./db')
// 2. 实例化app对象
const app = express()
// 3. 编写路由
/**
 * GET /users: 获取所有的用户信息
 * 返回: [{}, {}, {}]
 */
// app.get('/users', (req, res) => {
//   // 完成数据库的操作
//   let sql = `select * from users`
//   getAll(sql).then((data) => {
//     res.send(data)
//   })
// })

app.get('/users', async (req, res) => {
  // 完成数据库的操作
  let sql = `select * from users`
  const data = await getAll(sql)
  res.send(data)
})

/**
 * GET /users/:id 根据id获取单个用户的信息
 * 返回: {}
 */
// app.get('/users/:id', (req, res) => {
//   // 一. 解析请求参数, id
//   const { id } = req.params
//   // 二. 操作数据库
//   let sql = `select * from users where id=${id}`
//   getOne(sql).then((data) => {
//     res.send(data)
//   })
// })

app.get('/users/:id', async (req, res) => {
  // 一. 解析请求参数, id
  const { id } = req.params
  // 二. 操作数据库
  let sql = `select * from users where id=${id}`
  const data = await getOne(sql)
  res.send(data)
})
// 4. 监听端口
app.listen(3000)

5 使用async...await语法

// 1. 导入express包
const express = require('express')

// process是当前node的进程对象. cwd(current working directory)
// 在node中不推荐使用相对路径, 相对路径 相当于 cwd()
// console.log(process.cwd())
// 导入db(数据库操作的包)
/* 
db = {
  getAll,
  getById,
  exec,
}
*/
// const db = require('./db/index')
const { getAll, getById, exec } = require('./db/index')
// 2. 实例化app对象
const app = express()

// 处理请求体的数据. 使用express.json()中间件
app.use(express.json())
// 3. 路由
/**
 * 获取所有的用户信息
 * GET /users
 */
app.get('/users', async function (req, res) {
  // 编写sql语句
  let sql = 'select * from student'
  // 执行sql语句
  // getAll返回一个promise对象. 调用then方法得到data数据
  // await等待promise返回结果, 将结果作为表达式的值返回
  const data = await getAll(sql)
  res.send(data)
})

/**
 * 根据id获取用户信息
 * GET /users/:id
 */
app.get('/users/:id', async function (req, res) {
  // 一. 解析请求数据
  const id = req.params.id
  // 二. 操作数据库
  // 2.1 编写sql语句
  let sql = `select * from student where id=${id}`
  // 2.2 执行sql语句
  const data = await getById(sql)
  res.send(data)
})

/**
 * 新增用户
 * POST /users {name: 'test', age: 20}
 */
app.post('/users', async function (req, res) {
  // 一. 解析请求数据
  console.log(req.body)
  // 对象的解构, 将body对象解构出name和age两个变量
  const { name, age } = req.body
  // console.log(name, age)
  // 二. 操作数据库
  // 2.1 编写sql语句(插入3个点: 表, 字段, 值)
  let sql = `insert into student (name, age) values ('${name}', ${age})`
  // !!!!!!!!!!!!!! 重要调试技巧. 当sql执行出错时, 打印sql, 到控制台执行
  console.log(sql)
  // 2.2 执行sql语句
  const data = await exec(sql)
  res.send({
    id: data.insertId,
    name: name,
    age: age,
  })
})

/**
 * 修改用户
 * PUT /users/:id {name:'xiaoming-new', age: 21}
 */
app.put('/users/:id', async function (req, res) {
  // 一. 解析请求数据
  const id = req.params.id
  // 二. 操作数据库
  const { name, age } = req.body
  // 2.1 编写sql语句(更新4个点 表, 字段, 值, 条件)
  let sql = `update student set name='${name}', age=${age} where id=${id}`
  // 2.2 执行sql语句
  await exec(sql)

  res.send({
    id: id,
    name: name,
    age: age,
  })
})

/**
 * 删除用户
 * DELETE /users/:id
 */
app.delete('/users/:id', async function (req, res) {
  // 一. 解析请求数据
  const id = req.params.id
  // 二. 操作数据库
  // 2.1 编写sql语句
  let sql = `delete from student where id=${id}`
  // 2.2 执行sql语句
  await exec(sql)

  res.status(204).send('')
})
// 4. 监听端口
app.listen(3000)
  • 2
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值