简介
express的作用与Node.js内置的http模块类似,是专门用来创建Web服务器的。
常见的两种服务器
- Web网站服务器: 专门对外提供Web网页资源的服务器
- API接口服务器: 专门对外提供API接口的服务器
express基本使用
// 导入express
const express = require('express')
//创建web服务器
const app = express()
//监听get请求
app.get('/user/:id', (req, res) => {
//req.query 可以获取客户端发送过来的查询参数,默认是一个空对象
console.log(req.query)
//req.params 可以获取到URL中,通过: 匹配到的动态参数
console.log(req.params) // {id: '1'}
//res.send()向客户端响应一个对象,也可以响应一个字符串
res.send({
name: 'zhang',
age: 20
})
})
//监听post请求
app.post('/user', (req, res) => {
})
//启动web服务器
app.listen(80, () => {
console.log('express listen 80 on http://127.0.0.1')
})
使用express.static(): 快递托管静态资源
// 导入express
const express = require('express')
//创建web服务器
const app = express()
//express.static() 快速对外提供静态资源
//http://127.0.0.1/index.html 访问路径不需要添加pkg
// 托管多个静态目录时, 会按托管的先后顺序去查找资源
app.use(express.static('pkg'))
// http://127.0.0.1/pkg/index.html 访问路径需要添加pkg
app.use('/pkg', express.static('pkg'))
//启动web服务器
app.listen(80, () => {
console.log('express listen 80 on http://127.0.0.1')
})
nodemon 自动监听服务器
npm i -g nodemon
nodemon index.js
使用express路由精简项目结构
express中的路由分3部分
- 请求的类型
- 请求的URl地址
- 处理函数
app.METHOD(PATH, HANDLER) 是最简单的路由,直接将路由挂载在app上 express不建议将路由直接挂载到app上,而是推荐将路由抽离为单独的模块
- 创建路由模块对应的 .js文件
- 调用
express.Router()函数创建路由对象 - 向路由对象上挂载具体的路由
- 使用
module.exports向外共享路由对象 - 使用
app.use()函数注册路由模块
//router.js
const express = require('express')
const router = express.Router()
router.get('/user', (req, res) => {
res.send('get user')
})
router.post('/user/add', (req, res) => {
res.send('add a user')
})
module.exports = router
// index.js
// 导入express
const express = require('express')
//创建web服务器
const app = express()
// 导入路由模块
const userRouter = require('./router')
//使用app.use()注册路由模块
app.use(userRouter)
// 为路由模块添加统一的前缀
//app.use('/api', userRouter) // 访问时需要加水api
//启动web服务器
app.listen(80, () => {
console.log('express listen 80 on http://127.0.0.1')
})
app.use(): 注册全局中间件
常见的express中间件
next函数的作用:是实现多个中间件连续调用的关键
// 导入express
const express = require('express')
//创建web服务器
const app = express()
// 定义中间件函数
const mw = function (req, res, next) {
console.log('middleware')
next()
}
const mw2 = function (req, res, next) {
console.log('middleware2')
next()
}
const mw3 = function (req, res, next) {
console.log('middleware3')
next()
}
//注册为全局生效的中间件
app.use(mw)
// 注册路由中有/user的路由才会生效的中间件
app.use('/user', mw2)
app.use((req, res, next) => {
console.log('直接注册全局中间件')
next() //next一定要调用
})
//只在当前路由生效的中间件
app.get('/user', mw3, (req, res) => {
res.send('user')
})
//定义多个局部中间件
app.get('/', mw, mw2, (req, res) => { })
app.post('/', [mw2, mw3], (req, res) => { })
//启动web服务器
app.listen(80, () => {
console.log('express listen 80 on http://127.0.0.1')
})
中间件注意事项:
- 中间件不能定义在路由之后(错误级别的中间件除外)
- 可以连续调用多个中间件,且多个中间件间共享req和res对象
- 一定要调用next(),且next()后最好不要再写额外的代码
中间件的分类
- 应用级别
- 通过app.use()或app.get()或app.post(),绑定到app实例上的中间件
- 路由级别
- 绑定到express.Router()实例router上的中间件
- 错误级别
- 捕获项目中发生的异常 (err, req, res, next) => {}
- 错误级别中间件一定要放在所有路由之后
- express内置中间件
- express.static: 快速托管静态资源
- express.json: 解析JSON格式的请求体数据(4.16.0+)
- app.use(express.json())
- express.urlencoded: 解析URL-encode格式的请求体数据(4.16.0+)
- app.use(express.urlencoded({extended: false}))
- 第三方中间件
- body-parse 解析表单数据
- npm install body-parse
- const parse = require('body-parse')
- app.use(parse.urlencoded({extended: false}))
- express内置的express.urlencoded中间件,是基于body-parse这个第三方中间件进一步封装出来的。
- body-parse 解析表单数据
在NODE服务器,可以用
req.body这个属性,来接收客户端发送过来的请求体数据。在默认情况下,如果不配置解析表单数据的中间件(app.use(express.json())),则req.body默认等于undefined。 自定义中间件
- 定义全局中间件
- 监听req的data事件
- 监听req的end事件
- 使用querystring模块解析请求体数据
使用express创建API接口
在express中启用cors跨域资源共享
- npm install cors
- const cors = require('cors')
- app.use(cors()) 必须在配置CORS中间件之间声明JSONP接口
// html
$('#btnJSONP').on('click', function () {
$.ajax({
type: 'get',
url: 'http://127.0.0.1',
dataType: 'jsonp',
success: function (res) {
console.log(res)
}
})
})
//index.js
app.get('/api/jsonp', (req, res) => {
const functionName = req.query.callback
const data = { name: 'zs', age: 22 }
const scriptSrc = `${functionName}(${JSON.stringify(data)})`
res.send(scriptSrc)
})
操作mysql数据库
- 安装mysql
- npm install mysql
- 配置mysql模块
const mysql = require('mysql')
const db = mysql.createPool({
host: '127.0.0.1',
user: 'root',
password: 'root',
database: 'wechat'
})
db.query('select * from users', (err, rs) => {
if (err) {
return console.log(err.message)
}
console.log(rs[0])
})
const user = {username: 'zs', password: '123456'}
const sqlStr = 'insert into users (username, password) values (?, ?)'
db.query(sqlStr, [user.username, user.password], (err, rs) => {
if (err) {
return console.log(err.message)
}
if(rs.affectRows === 1){
console.log('插入数据成功')
}
})
// 插入数据对象与表单一一对应,可以简写
const sqlStr2 = 'insert into users values ?'
db.query(sqlStr2, user, (err, rs) => {
if (err) {
return console.log(err.message)
}
if(rs.affectRows === 1){
console.log('插入数据成功')
}
})
var mysql = require('mysql');
var pool = mysql.createPool({
host: 'localhost',
port: 3306,
user: 'root',
password: 'root',
database: 'wechat',
});
//常规SQL
let query = function (sql, arr = [], callback) {
//建立链接
pool.getConnection(function (err, connection) {
if (err) throw err;
connection.query(sql, arr, function (error, results, fields) {
//将链接返回到连接池中,准备由其他人重复使用
connection.release();
if (error) throw error;
//执行回调函数,将数据返回
callback && callback(results);
});
});
};
let pquery = function (sql, arr = []) {
return new Promise((resolve, reject) => {
pool.getConnection(function (err, connection) {
if (err) throw err;
connection.query(sql, arr, function (error, results, fields) {
//将链接返回到连接池中,准备由其他人重复使用
connection.release();
if (error) throw error;
//执行回调函数,将数据返回
// callback && callback(results);
// console.log(results)
resolve(results);
});
});
})
}
module.exports = {
query,
pquery,
pool
}
密码加密 bcryptjs
npm i bcryptjs@2.4.3
const bcrypt = require('bcrypt')
pwd = bcrypt.hashSync(pwd, 10)
bcypt.compareSync()
表单验证
- @escook/express-joi
- @hapi@joi@17.1.0 定义验证规则
前后端身份认证
- 服务端渲染: Session认证机制
- 前后端分离: JWT认证机制
Cookie
不同域名下的Cookie各自独立,每当客户端发起请求时,会自动把当前域名下的所有未过期的Cookie一桶发送到服务器
- 自动发送
- 域名独立
- 过期时限
- 4kb限制
- 不具有安全性
客户端第一次请求服务器时,服务器通过
响应头的形式,想客户端发送一个身份认证的Cookie,客户端会自动将Cookie保存在浏览器中。随后,当客户端浏览器每次请求服务器的时候,浏览器会
自动将身份认证相关的Cookie,通过请求头的形式发送给服务器,服务器即可验明客户端的身份
Session
当前端请求后端接口不存在跨域问题的时候,推荐使用Session身份认证机制。存在跨域问题的时候,推荐使用JWT认证机制。 express-session
const express = require('express')
const app = express()
const session = require('express-session')
app.use(session({
secret: 'keyboard cat', //secret可以为任意字符串
resave: false,
saveUninitialized: true
}))
app.post('/api/login', (req, res) => {
if (req.body.username !== 'admin' || req.body.password !== '000000') {
return res.send({ status: 1, msg: '登录失败' })
}
// 只有成功配置了express-session中间件后,才能往req.session中存数据
req.session.user = req.body
req.session.isLogin = true
res.send({ status: 0, msg: '登录成功' })
})
app.get('/api/username', (req, res) => {
if (!req.session.isLogin) {
return res.send({ status: 1, msg: 'fail' })
}
res.send({
status: 0, msg: 'success', username: req.session.user.username
})
})
app.post('/api/logout', (req, res) => {
req.session.destroy() //情况session信息
res.send({
status: 0,
msg: '退出登录成功'
})
})
app.listen(80, () => {
console.log('http://127.0.0.1')
})
JWT认证机制(JSON Web Token)
JWT是目前最流行的跨域认证解决方案
JWT工作原理: 用户的信息通过Token字符串的形式,保存在客户端浏览器中。服务器通过还原Token字符串的形式来认证用户的身份。 JWT的组成:Header.Payload.Signature
- Header(头部): 负责安全性
- Payload(有效荷载): 真正的用户信息,是用户信息经过加密之后生成的字符串
- Signature(签名): 负责安全性
客户端收到服务器返回的JWT之后,通常会将它存储在localStorage或sessionStorage中。此后,客户端每次与服务器通信,都要带上这个JWT的字符串,从而进行身份认证。推荐的做法是把JWT放在HTTp请求头的Authorization字段中:
Authorization: Bearer <token>
安装JWT相关的包
- jsonwebtoken:生成JWT字符串
- sign(): 将用户的信息加密成JWT字符串,响应给客户端
- 参数1: 用户的信息对象
- 参数2: 密钥
- 参数3: 配置有效期
- sign(): 将用户的信息加密成JWT字符串,响应给客户端
- express-jwt: 将JWT字符串解析还原成JSON对象