EXPRESS

162 阅读7分钟

简介

express的作用与Node.js内置的http模块类似,是专门用来创建Web服务器的。

常见的两种服务器

  1. Web网站服务器: 专门对外提供Web网页资源的服务器
  2. 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部分

  1. 请求的类型
  2. 请求的URl地址
  3. 处理函数

app.METHOD(PATH, HANDLER) 是最简单的路由,直接将路由挂载在app上 express不建议将路由直接挂载到app上,而是推荐将路由抽离为单独的模块

  1. 创建路由模块对应的 .js文件
  2. 调用 express.Router()函数创建路由对象
  3. 向路由对象上挂载具体的路由
  4. 使用 module.exports向外共享路由对象
  5. 使用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这个第三方中间件进一步封装出来的。

在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数据库

  1. 安装mysql
    • npm install mysql
  2. 配置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

  1. Header(头部): 负责安全性
  2. Payload(有效荷载): 真正的用户信息,是用户信息经过加密之后生成的字符串
  3. Signature(签名): 负责安全性

客户端收到服务器返回的JWT之后,通常会将它存储在localStorage或sessionStorage中。此后,客户端每次与服务器通信,都要带上这个JWT的字符串,从而进行身份认证。推荐的做法是把JWT放在HTTp请求头的Authorization字段中:Authorization: Bearer <token>

安装JWT相关的包

  • jsonwebtoken:生成JWT字符串
    • sign(): 将用户的信息加密成JWT字符串,响应给客户端
      • 参数1: 用户的信息对象
      • 参数2: 密钥
      • 参数3: 配置有效期
  • express-jwt: 将JWT字符串解析还原成JSON对象