一. Express介绍
1 概念
Express 是一个基于nodejs平台的快速,开发,极简的web开发框架
理解: 它和之前内置的http模块一样的,在内置http模块的基础做了封装,使用更加快捷方便
2 核心内容(作用)
Express的核心只有两个部分
- 路由
- 中间件
Express提供了基本的路由处理和中间件功能, 几乎所有功能的实现都由一个个独立的中间件完成
路由
根据不同的url路径,调用后台不同的处理函数
中间件
指从请求到响应这个业务流程的所有中间处理环节(函数)
二. 安装及使用
1 安装
npm i express
2 使用
步骤
- 导入express包
- 实例化对象
- 编写路由(中间件)
- 监听端口
示例
// 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路径,调用后台不同的处理函数
1 请求方式
请求方式就是HTTP协议的请求方式, 常见的有
- get: 对应
app.get()
- post: 对应
app.post()
- put: 对应
app.put()
- delete: 对应
app.delete()
2 URL
格式
协议://端口/path?queryString#hash #hash:锚链接
解析queryString
对于query参数
使用req.params获得参数
可得到如:id,name,age…
对于/users/:id 类型
使用req.query获得参数
可得到如:id,name,age…
// 第一种, 不带参数
app.get('/users', function(req, res) {
res.send('hello world')
})
// 第二种, 带参数
app.get('/users/:id', function(req, res) {
res.send('hello world')
})
// 第三种, 正则表达式, 以html结尾
app.get(/.html$/, function(req, res) {
res.send('hello world')
})
3 处理函数
1) 请求对象
请求对象包含了一次请求中的所有数据(http请求头, 请求参数…)
get请求
获取所有用户
app.get(‘/users’, function (req, res) {}
根据id获取用户(某一个用户) 带参数
app.get(‘/users/:id’, function (req, res) {}
遍历数组 forEach/filter/for in/for of/for
app.get('/', function (req, res) {
console.log(req)
})
// 1. 导入express包
const express = require('express')
// 2. 实例化app对象
const app = express()
// 3. 编写路由
// 模拟数据库
const db = [
{ id: 1, name: 'xiaoming', age: 20 },
{ id: 2, name: 'xiaomei', age: 18 },
{ id: 3, name: 'xiaopang', age: 2 },
]
// 用户模块
/**
* 获取所有用户
* GET /users
*/
app.get('/users', function (req, res) {
// 对于query参数, 使用req.query获得参数
console.log(req.query)
// 是一个数组
res.send(db)
})
/**
* 根据id获取用户
* GET /users/:id
* GET /users/1
* 在req.params中形成{id: 1}
*/
app.get('/users/:id', function (req, res) {
// 通过req.params(路由参数)得到id的值
// 解析get参数
console.log(req.params.id) // {id: 1}
// 数组的forEach. 数组的filter, for in for of
for (let i = 0; i < db.length; i++) {
if (db[i].id == req.params.id) {
res.send(db[i])
}
}
})
post请求
- app.post(‘/users’, function (req, res) {}
- 添加data end 两个事件
- 将postdata转换成json对象
- 给新添加的用户添加id属性
将添加id后的用户添加到数据库
app.post('/users', function (req, res) {
// 解析请求体中的数据
let postData = ''
req.on('data', (data) => (postData += data))
req.on('end', () => {
console.log(postData)
console.log(typeof postData) // string
// 将postData(JSON格式的字符串) 转换成 JSON对象
var user = JSON.parse(postData) // {name: 'xiaoming', age: 20}
user.id = db.length + 1 // 最好使用guid(全球唯一的id, 不会重复)
// 添加到db数据库
db.push(user)
res.send(db)
})
})
put请求
app.put(‘/users/:id’, function (req, res) {}
- 解析请求数据 如:id
- 获取解析数据的属性 如:name/age
- 添加data end事件
- 将其转为JSON对象
- 遍历数组
- 查找对应id值相等的对象
修改该对象的name/age等属性值,更新数组
/**
* 修改用户
* PUT /users/:id 请求参数 {name: 'xiaoming-new', age: 21}
*/
app.put('/users/:id', function (req, res) {
// 解析请求的数据
// 获取id的值
const id = req.params.id
// 获取name和age的值
let postData = ''
req.on('data', (data) => {
postData += data
})
req.on('end', () => {
const user = JSON.parse(postData)
console.log(user)
// 查找db数组中.id == 传递的id的对象, 更新数组
db.forEach((item) => {
if (item.id == id) {
item.name = user.name
item.age = user.age
}
})
// 将更新后的结果返回
res.send(db)
})
})
delete请求
app.delete(‘/users’, function (req, res) {}
- 解析请求参数
- 遍历forEach db数组
- 查找该参数对应在db中的数据,将其删除
array.splice(start, deleteCount) 删除数组中特定索引
satrt 索引
deleteCount 删除的个数
如: array.splice(2,3)
从数组索引为2开始删除3个数
/**
* 删除用户
* DELETE /users/:id
*/
app.delete('/users/:id', function (req, res) {
// 解析请求参数
const id = req.params.id
// 查找db中的数据, 将其删除
db.forEach((item, index) => {
if (item.id == id) {
// 数据的删除操作(第一个参数是索引index, 第二个参数是删除的个数)
db.splice(index, 1)
res.send(db)
}
})
})
2) 获取请求参数
语法
// get请求
req.query.参数名
示例: get请求
// 如果url是: /users/1, 通过params获取
app.get('/users/:id', function(req, res) {
res.send(req.params.id)
})
// 如果url是: /users?id=1, 通过query获取
app.get('/users', function(req, res) {
res.send(req.query.id)
})
示例: post请求
// 1. 导入express包
const express = require('express')
// 2. 实例化对象
const app = express() // app是一个对象
// 3. 编写路由(中间件)
app.post('/', function(req, res) {
let postData = ''
req.on('data', data => postData += data)
req.on('end', () => {
res.send(JSON.parse(postData))
})
})
// 4. 监听端口
app.listen(3000, function () {
console.log('server is running on http://localhost:3000')
})
根据id查找对应的数据并返回
编写post.http
POST http://localhost:3000/
Content-Type: application/json
{
"name": "xiaoming"
}
3) 响应对象
响应对象用于向客户端返回数据, 在处理函数中需要调用以返回数据
常用的有两个
- res.send(): 返回各种类型
-
四. 中间件
1 概念及作用
概念
中间件是在请求和响应中间的处理程序, 是一个个功能独立的函数
作用
扩展原始程序的插件2 中间件类型
A. Application-level middleware 应用级中间件
app.use()
app.method()
B. Router-level middleware 路由级中间件
操作步骤
1,编写routes/路由文件- **导入express包**
- **创建路由对象**
- **编写相关路由**
- **导出路由对象**
2,在入口文件中导入路由对象
3,注册路由中间件
C. Error-handling middleware 错误处理中间件
D. Built-in middleware 内置中间件
express.json
让程序支持JSON格式的请求体
app.use(express.json( ))
express.urlencoded
让程序支持urlencoded格式的请求体
app.use(express.urlencoded( ))
E. Third-party middleware 第三方中间件
3 应用级中间件
在Express中, 使用app.use
或者app.METHOD
注册的中间件叫做应用级中间件
中间件就是一个函数
app.use('path', function (req, res, next) {
next()
})
注意
在中间件中需要通过调用next()执行下一个中间件
如果不执行next(), 也没有调用send(). 这次请求将会被挂起
app.use( )
全局中间件
会给每一个路由规则都添加一个中间件处理函数
app.use( )
一般写在路由的前面
// 不写第一个参数, 给所有访问都注册了一个中间件
app.use(function (req, res, next) {
console.log('Time:', Date.now())
next()
})
app.method( )
局部中间件
给一个特定的路由规则添加中间件处理函数
app.method(‘/‘, function (req, res, next) { next( ) }
在实际项目中,多用app.use( )写局部中间件
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)
4 路由级中间件
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入口会使用文件过大, 不好维护. 可以把不同的路由拆分成多个模块
app.js
// 1. 导入express的包
const express = require('express')
// 2. 实例app对象
const app = express()
// 3. 编写路由
// 用户模块 5个接口
const usersRouter = require('./routes/users.js')
app.use(usersRouter)
// 文章模块 5个接口
const articlesRouter = require('./routes/articles.js')
app.use(articlesRouter)
// 4. 监听端口
app.listen(3000)
routes/users.js
// 以/users开头的接口
// 一. 导入express包
const express = require('express')
// 二. 创建路由对象
const router = express.Router()
// 三. 编写跟/users相关的路由
router.get('/users', function (req, res) {
res.send('users')
})
router.post('/users', function (req, res) {
res.send('user')
})
// 四. 导出路由对象
module.exports = router
routes/articles.js
// 以/articles开头的接口
// 一. 导入express包
const express = require('express')
// 二. 创建Router对象
const router = express.Router()
// 三. 编写跟article相关的路由
router.get('/articles', function (req, res) {
res.send('articles')
})
// 四. 导出router对象
module.exports = router
5 内置中间件
Built-in middleware 内置中间件
express.json
让程序支持JSON格式的请求体
app.use(express.json( ))
express.urlencoded
让程序支持urlencoded格式的请求体
app.use(express.urlencoded( ))
// 内置中间件
// express.json()
// express.urlencoded()
// 中间件的作用: 扩展原始程序的插件
const express = require('express')
const app = express()
// 让所有的路由都支持 解析请求体数据
app.use(express.json()) // 让程序支持json格式的请求体
app.use(express.urlencoded()) // 让程序支持urlencoded格式的请求体
app.post('/users', function (req, res) {
console.log(req.body)//{ name: 'xiaoming', age: 20 }
})
app.put('/users/:id', function (req, res) {
console.log(req.body)//{ name: 'xiaoming-new', age: 21 }
})
app.listen(3000)
五. 数据库操作
1 安装mysql库
参照npm.mysql官方文档
npm i mysql
2 入门案例
操作数据库, 就是模拟客户端. 向MySQL的服务端发送SQL语句. 基本步骤如下:
- 引入mysql包
- 创建数据库连接
- 连接数据库
- 执行SQL查询
- 关闭连接
//1,导入mysql包(masql客户端)
const { createConnection } = require('mysql');
const mysql = require('mysql');
const Connection = require('mysql/lib/Connection');
//2,创建连接
const con = mysql.createConnection({
host: '127.0.0.1',
port: 3306,
user: 'root', //数据库用户名
password: '1234',
database: 'users',
});
//3,连接数据库
con.connect()
//4,执行sql语句
con.query('select * from student',function(err,data){
if(err){
//查询错误, 抛出异常
throw err
}
//没有异常就打印数据库服务端返回的数据
console.log(data);
})
//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: 'user',
})
// 3. 连接
con.connect()
// 4. 操作
// 4.1 获取所有的数据
// function getAll(sql, callback) {
// // sql = 'select * from student'
// // callback = function (abc) {console.log(abc)}
// con.query(sql, function (err, data) {
// if (err) throw err
// // callback(data) --> 调用callback函数
// //(function (abc) {console.log(abc)})(data)
// callback(data)
// })
// }
function getAll(sql) {
return new Promise((resolve, reject) => {
con.query(sql, function (err, data) {
if (err) reject(err)
resolve(data)
})
})
}
// 4.2 根据id获取单个数据
function getById(sql) {
return new Promise((resolve, reject) => {
con.query(sql, function (err, data) {
if (err) reject(err)
data[0] ? resolve(data[0]) : resolve(null)
})
})
}
// 4.3 写入操作(增/删/改)
function exec(sql) {
return new Promise((resolve, reject) => {
con.query(sql, function (err, data) {
if (err) reject(err)
resolve(data)
})
})
}
// 5. 导出一个对象
module.exports = {
getAll,
getById,
exec,
}
封装后的操作
// 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', function (req, res) {
// 编写sql语句
let sql = 'select * from student'
// 执行sql语句
// getAll返回一个promise对象. 调用then方法得到data数据
getAll(sql).then((data) => {
res.send(data)
})
})
/**
* 根据id获取用户信息
* GET /users/:id
*/
app.get('/users/:id', function (req, res) {
// 一. 解析请求数据
const id = req.params.id
// 二. 操作数据库
// 2.1 编写sql语句
let sql = `select * from student where id=${id}`
// 2.2 执行sql语句
getById(sql).then((data) => {
res.send(data)
})
})
/**
* 新增用户
* POST /users {name: 'test', age: 20}
*/
app.post('/users', 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语句
exec(sql).then((data) => {
// 成功时返回的对象
console.log(data)
res.send({
id: data.insertId,
name, // name: name(当属性名和值相同时, 可以简写)
age: age,
})
})
})
/**
* 修改用户
* PUT /users/:id {name:'xiaoming-new', age: 21}
*/
app.put('/users/:id', 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语句
exec(sql).then((data) => {
console.log(data)
// 返回. 修改后的数据
res.send({
id: id,
name: name,
age: age,
})
})
})
/**
* 删除用户
* DELETE /users/:id
*/
app.delete('/users/:id', function (req, res) {
// 一. 解析请求数据
const id = req.params.id
// 二. 操作数据库
// 2.1 编写sql语句
let sql = `delete from student where id=${id}`
// 2.2 执行sql语句
exec(sql).then((data) => {
console.log(data)
res.status(204).send('')
})
})
// 4. 监听端口
app.listen(3000)
5 使用async await简化
同步: 排队 (javascript就是典型的同步代码, 按顺序执行)
异步: 客户端向服务器发送请求的时候, 用户可以进行其他的操作 (互联网上对数据的操作都是异步的)
1 async介绍
// async: 异步, 写在function关键字的前面
// 同步函数
function a() {
return 123
}
console.log(a())
// 使用async修饰函数, 使其做为一个异步函数
// 1. 可以单独使用
// 2. 让函数返回一个promise对象
async function b() {
// Promise.resovle(123)
return 123
}
const p = b()
// promise(只有两个属性: result/status)
console.log(p)
p.then((data) => {
console.log(data) // 123
})
2 await介绍
// 1. await不能单独使用, 必须跟async连用. 存在于异步函数中
// 2. await关键字后面跟一个promise对象
// 关键字后面不是promise, 自动转换成promise对象
// 3. await表达式, 返回promise的结果
;(async function () {
const p = new Promise((resovle, reject) => {
resovle(123)
})
// p.then((data) => {
// console.log(data)
// })
const test = await p // 123
console.log(test)
})()
<script>
// async
/*
同步: 排队
异步: 客户端向服务器发送请求的时候, 用户可以进行其他的操作 (互联网上对数据的操作都是异步的)
*/
/* 1.模拟同步 */
/* console.log(123);
console.log(245);
log; //执行到这里就会报错, 后面的不会执行
console.log(666); */
/* 2.模拟异步 */
console.log(1);
setTimeout(() => {
console.log(2);
});
Promise.resolve(100).then((res) => {
console.log(res);
});
console.log(3);
// 打印顺序: 1-3-100-2 Promise执行的优先级别比setTimeout高
/* 3.async异步 */
// 如果函数前面加上async,name得到的返回值, 就是promise
async function go() {
return 10;
}
var res = go();
console.log(res); //Promise {<fulfilled>: 10}
/* 4.await */
// await可以获取promise--resolve的值 (相当于调用then方法)
// await获取promise--reject的值 可以使用 try-catch语句
// await只能在async函数中使用
var p = new Promise((resolve, reject) => {
resolve(100);
});
async function getRes() {
var res = await p;
console.log(res);
}
getRes(); //100
var p = new Promise((resolve, reject) => {
reject(200);
});
async function getRes() {
try {
let res = await p;
console.log(res);
} catch (err) {
console.log(err);
}
}
getRes(); //200
</script>
3 进行简化
// 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)