Express
什么是 Express?
Express 是基于 Node.js 平台, 快速,开放,极简的 Web 开发框架
基本使用
安装
在项目所处的根目录中运行:
npm i express@4.17.1
创建基本的 Web 服务器
//导入 express const express = require('express') // 创建 web 服务器 const app = express() // app.get('/user/:id', (res, req) => { res.send() //向客户端响应文本或字符串 req.query() //默认是个空对象;可以获取到客户端发送过来的查询参数例 ?name=zs&age=20 req.params //默认空对象,可以访问到URL中,通过: 匹配到的动态参数[:id] // :必须写 id可以任意修改,它是参数的名字 //还可以写多个 'user/:id/:name/:age' }) // 调用 app.listen(端口号, 启动成功后的回调函数) ,启动服务器 app.listen(80,() => { console.log('express server running at http://127.0.0.1') })
监听 GET 请求
app.get('URL', function(res, req) { }) // res : 请求对象(包含与请求相关的属性和方法) // req : 响应对象(包含与响应相关的属性和方法)
监听 POST 请求
app.post('URL', function(res, req) { })
express.static()
通过该函数我们可以非常方便的创建一个静态资源服务器,实现某个目录中的文件对外开放访问;存放静态文件的目录名不会出现在 url 中
app.use(express.static('public'))
托管多个静态资源
即多次调用 express.static() 函数
访问静态资源文件时, express.static() 函数会根据目录的添加顺序查找所需的文件
ALT+ shang
挂载路径前缀
app.use('/abc',express.static('./abc'))
路径前 挂载 前缀
nodemon
监听项目文件的变动,当代码被修改后, nodemon 会自动帮我们重启项目
安装
npm install -g nodemon
使用
将 node 改为 nodemon
路由
express中的路由就是映射关系: 客户端的请求与服务器处理函数之间的映射关系
分为三部分: 请求的类型(METHOD), 请求的URL 地址(PATH), 处理函数(HANDLER):
app.METHOD(PATH, HANDLER)
路由的匹配
- 按照定义的先后顺序进行匹配
- 请求类型和请求的URL 同时匹配成功,才会调用对应的处理函数
模块化路由
//导入 express const express = require('express') //创建路由对象 const router = express.Router() //挂载具体的路由 router.get('/', (req, res) => { }) router.post('/',(req, res) => { }) //向外导出路由对象 module.exports = router
注册路由模块
const express = require('express') const app = express() const router = require('./url') //注册路由模块 app.use(router) app.use() //函数作用,注册全局中间件
为路由模块添加前缀
app.use('/abc',router)
中间件
格式
function(req, res, next)
next函数的作用
next 函数是实现多个中间件连续调用的关键, 他表示把流转关系转交给下一个中间件或路由.
如何使用中间件
const express = require('express') const app = express() //定义一个最简单的中间件函数 const mw = function(req, res, next) { //把流转关系转交给下一个中间件或路由 next() } app.listen(80,() => { })
全局生效的中间件
客户端发起的任何请求, 到达服务端后,都会触发的中间件,叫做全局生效的中间件
app.use(mw)
简化形式全局中间件
app.use((req, res, next) => { next() })
作用:
多个中间件之间,共享同一份 req 和 res.基于这样的特性,我们可以在上游的中间件中, 统一为 req 和 res 对象添加自定义属性或方法,供下游的中间件或路由进行使用
局部生效的中间件
不使用 app.use() 的中间件
const express = require('express') const app = express() const mw1 = (req ,res ,next) => { next() } app.get('/',mw1,(req, res) => { //mw1只在此函数中生效 //若有多个局部中间件 添加到mw1后面,用逗号(,)隔开 }) app.post('/',(req, res) => { }) app.listen(80, () => { })
中间件的使用注意事项
- 一定要在路由之前注册中间件
- 可以连续调用多个中间件进行处理
- 不要忘记调用 next() 函数
- 为了防止代码逻辑混乱,调用next() 函数后不要再写额外的代码
- 连续调用多个中间件时, 多个中间件之间, 共享 req 和 res 对象
中间件的分类
路由级别的中间件
错误级别的中间件
//定义错误级别的中间件, 捕获整个项目的异常错误, 从而防止程序的崩溃 app.use(err, req, res, next) => { res.send('Error:' + err.message) } //错误级别中间件要放在所有路由之后
EXpress内置中间件
- express.static
- express.json 解析 JSON 格式的请求体数据
- express.urlencoded 解析 URL-encoded 格式的请求体数据
req.body 属性,接受客户端发送过来的请求体数据;
如果不配置解析表单数据的中间体,则 req.body 默认等于 undefined
app.use(express.json()) //内置中间件 app.use(express.urlencoded({extended:false}))
在路由中 使用 req.body
数据库的使用
pk: 主键, 唯一标识符
NN: 值不允许为空
UQ: 值唯一
AI: 值自动增长
Default/Expression : 默认值
什么是SQL?
SQL 是结构化查询语言, 专门用来访问和处理数据库的编程语言.能够让我们以编辑的形式, 操作数据库里面的数据.
--
注释
从 FROM 指定的[表中],查询出[所有的]数据, * 表示[所有列]
select * from 表名称
从 FROM 指定的[表中], 查询出指定列名称的数据
select 列名称 from 表名称
向数据表中插入新的数据行
insert into 表名称(列1,列2...) values (值1, 值2,...)
修改表中的数据
update 表名称 set 列名称 = 新值 where 列名称 = 某值
多个列更改,用逗号隔开
- update 指定更新哪个数据
- SET 指定列对应的新值
- 用 where 指定更新的条件
删除表中的行
delete form 表名称 where id=
关键字对大小写不敏感
where子句
where 子句用于限定选择的标准 .
<>和!=都表示不等号
and 必须同时满足多个条件
or 任一条件满足即可
order by 根据指定的列对结果集进行排序,默认按升序(ASC)排序(DESC关键字,可以降序排序)
select * from users order by id
默认升序
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tM1nzSVY-1647130047397)(C:\Users\Lenovo\Desktop\typora\img\orderby.png)]
select * from users order by id desc
降序
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e8nLNXfo-1647130047399)(C:\Users\Lenovo\Desktop\typora\img\orderbydesc.png)]
多个排序规则时,用逗号分隔
count(*)函数
count(*)函数用于返回查询结果的总数据条数
select count(*) from 表名称
as 关键字
给列起别名
select username as uname, password as upwd from users
在项目中操作 MySQL
npm install mysql
建立与 mysql 数据库的连接
const db = mysql.createPool({ host: '127.0.0.1', //数据库的 IP 地址 user: 'root', //登录数据库的账号 password: 'admin123', //登录数据库的密码 database: 'my_db_01' //指定要操作那个数据库 })
测试 MySQL 模块是否正常工作
db.query('select 1',(err, results) => { if(err) return console.log(err.message) console.log(results) })
查询 users 表中所有数据
const sqlStr = 'select * from users' db.query(sqlStr, (err, result) => { if(err) return console.log(err.message) //如果使用 select 查询语句,执行的结果为 数组 console.log(result) })
插入数据
//要插入到 users 表中的数据对象 const user = { username: 'Spider-Man', password: 'pcc121'} //待执行的 SQL 语句, 其中英文的 ? 表示占位符 const sqlStr = 'insert into users(username, password) value(?, ?)' //使用数组的形式依次为 ? 占位符指定具体的值 db.query(sqlStr, [user.username, user.password], (err, results) => { if(err) return console.log(err.message) //如果执行 insert into 插入语句, 则 results 是一个对象 if(results.affectedRows === 1) { //判断是否插入成功 console.log('插入数据成功') } })
插入数据的便捷方式
//要插入到 users 表中的数据对象 const user = { username: 'Spider-Man', password: 'pcc121'} //待执行的 SQL 语句, 其中英文的 ? 表示占位符 const sqlStr = 'insert into users SET ?' //直接将数据对象作为占位符 db.query(sqlStr, user, (err, results) => { if(err) return console.log(err.message) //如果执行 insert into 插入语句, 则 results 是一个对象 if(results.affectedRows === 1) { //判断是否插入成功 console.log('插入数据成功') } })
更新数据
const user = {id: 6, username: 'aaa', password: '000'} const sqlStr = 'update users set username=?, password=? where id=?' db.query(sqlStr, [user.username, user.password, user.id], (err, results) => { if(err) return console.log(err.message) //如果执行 insert into 插入语句, 则 results 是一个对象 if(results.affectedRows === 1) { //判断是否插入成功 console.log('更新成功') } })
更新数据的便捷方式
// 要更新的数据对象 const user = { id = 7, username: 'aaaa', password: '0000'} // 要执行的 sql 语句 const sqlStr = 'update users set ? where id=?' //调用 db.query() 执行sql语句,同时使用数组依次为占位符指定具体的值 db.query(sqlStr, [user,user.id], (err, results) => { if(err) return console.log(err.message) if(results.affectedRows === 1) { console.log('更新数据成功') } })
删除数据
const delete = 'delete from users where id=?' db.query(sqlStr, 5, (err, results) => { if(err) return console.log(err.message) if(results.affectedRows === 1) { console.log('删除数据成功') } })
标记删除
const sqlStr = 'update users set status=? where id=?' db.query(sqlStr, [1, 6], (err, results) => { if(err) return console.log(err.message) if(results.affectedRows === 1) { console.log('删除数据成功') } })
前后端的开发模式
身份认证
服务器端渲染使用 Session 认证机制
HTTP 协议的无状态性指的是客户端的每次 HTTP 请求都是独立的, 连续多个请求之间没有直接的关系, 服务器不会注定保留每次 HTTO 请求的状态.
如何突破 HTTP 无状态的限制?
Cookie 是存储在用户浏览器中的一段不超过 4kb 的字符串, 它由一个名称, 一个值和其他几个用于控制 Cookie 有效期,安全性,使用范围的 可选属性组成.
不同域名下的 Cookie 各自独立, 每当客户端发起请求时, 会自动把当前域名下所有未过期的 Cookie 一同发送到服务器.
Cookie 的几大特性: 1. 自动发送 2. 域名独立 3. 过期时限 4. 4KB 限制
在身份认证中的作用: 客户端第一次请求服务器的时候, 服务器通过响应头的形式, 向客户端发送一个身份认证 Cookie , 客户端会自动将 Cookie 保存在浏览器中.
随后, 当客户端浏览器每次请求服务器的时候, 浏览器会自动将身份认证相关的 cookie ,通过请求头的形式发送给服务器 服务器即可查明 客户端的身份
Cookie 不具有安全性, 千万不要使用 Cookie 存储重要且隐私的数据!
提高身份认证的安全性
session 的工作原理
安装 express-session
npm install express-session
配置
const express = require('express') const session = require('express-session') const app = express() app.use( session({ secret: 'keyboard cat', resave: false, saveUninitialized: true }))
存储信息
req.session.user = req.body
req.sesion.islogin = true
清空 session
req.session.destroy()
清空服务器保存的 session 信息
前后端分离使用 JWT 认证机制
JWT组成的三部分:
Header(头部).Payload(有效荷载).Signature(签名)
三者间用 . 隔开
Payload 部分才是用户的真实信息, 它是用户信息经过加密之后生成的字符串
Header 和 Signature 是安全性相关的部分 , 只是为了保证 Token 的安全性
使用方式
Authorization: Bearer <token>
安装 JWT 相关的包
npm install jsonwebtoken express-jwt
jsonwebtoken 用于生成 JWT 字符串
express-jwt 用于将JWT 字符串解析还原成 JSON 对象
定义 secret 密钥
为了保证 JWT 字符串的安全性, 我们专门定义了一个用于 加密 和 解密 的 sercet 密钥:
- 当生成 JWT 字符串的时候, 需要使用 secret 密钥对用户的信息进行加密, 最终得到加密好的JWT 字符串
- 当把 JWT 字符串解析还原为 JSON 对象时, 需要使用 secret 密钥进行解密
const secreKey = 'itheima No1 ^_^'
在登陆后生成 JWT 字符串
参数一:用户的信息对象
参数二: 加密的密钥
参数三: 配置对象, 可以配置当前 token 字符串
sign({username: userinfo.username}, secretKey, { expiresIn: '100s'})
将 JWT 字符串还原为 JSON 对象
req.user()获取用户信息