Express简介

一、Express

1.1 简介

  • 官网:Express
  • 概念:基于Node.js平台,快速、开放、极简的Web开发框架
  • 作用:
    • 创建web网站服务器
    • 创建API接口服务器

1.2 基本使用

  • 相关命令
# 包初始化
npm init 
# 安装
npm install express
npm i nodemon -g
# 启动
nodemon index.js # 或 node --watch index.js
  • 相关方法
// 1.get,post请求
app.get('请求url', (req, res) => {})
app.post('请求url', (req, res) => {})
// 2.把内容响应给客户端
res.send({ name: '张三', age: 18 })
// 3.获取url中携带的查询参数(/user?name=zs&age=18)
req.query // { name:zs, age: 18 }
// 4.获取url中的动态参数(/user/1)
app.get('/user/:id', (req, res) => {
    console.log(req.params) // { id: '1' }
    res.send({})
})

1.3 托管静态资源

  • express.static
// 1.托管静态资源目录
app.use(express.static('public'))
// 设置后直接访问public目录下的所有文件
// http://localhost:3000/image/bg.jpg
// http://localhost:3000/css/index.css

// 2.挂载路径前缀
app.use('/public', express.static('public'))
// http://localhost:3000/public/image/bg.jpg
// http://localhost:3000/public/css/index.css

// 3.托管多个
app.use(express.static('public'))
app.use(express.static('file'))
  • 注意事项
    • 存放静态文件的目录名不会出现在URL中
    • 托管多个时会根据目录的添加顺序查找所需要的文件

二、Express 路由

2.1 路由概念

  • 定义:客户端请求与服务端处理函数之间的映射关系
  • 格式:app.METHOD(PATH, HANDLER)
  • 匹配规则:
    • 按照定义的先后顺序匹配
    • 请求类型和请求url同时匹配成功,才会调用对应处理函数

2.2 路由使用

  • 基础使用
// app上挂载路由
app.get('/', (req, res) => { res.send('hello world') })
app.post('/', (req, res) => { res.send('post') })
  • 模块化路由
// 1.新建userRouter.js文件
const express = require('express');
const router = express.Router();
router.get('/user', (req, res) => {
    res.send('get请求')
})
router.post('/user', (req, res) => {
    res.send('post请求')
})
module.exports = router;

// 2.index.js 注册路由模块
const userRouter = require('./userRouter');
// 3.路由模块添加前缀(可选)访问 http://127.0.0.1:8000/api/user
app.use('/api', userRouter);

三、Express 中间件

3.1 中间件概念

  • 定义:业务流程的中间处理环节
  • 格式:const mw = function(req, res, next){ next() }
  • 注意:
    • 中间件函数必须包含next参数,路由处理函数只有req,res
    • next函数作用是把流转关系转交给下一个中间件或路由

3.2 中间件初体验

  • 全局生效的中间件(任何请求都会触发的中间件)
const mw = function(req, res, next){
    console.log('这是一个中间件函数');
    next();
}
app.use(mw);

// 简写
app.use((req, res, next) => { 
    console.log('调用第一个全局中间件');
    next() 
})
app.use((req, res, next) => { 
    console.log('调用第二个全局中间件');
    next() 
})
  • 局部生效的中间件(不使用app.use定义的中间件,且只在当前路由生效)
const mw = function(req, res,next) {
    console.log('这是中间件函数');
    next();
}

const mw1 = function(req, res,next) {
    console.log('这是中间件函数2');
    next();
}
router.get('/user', mw, mw1, (req, res) => {
    res.send('get请求')
})
// 等同于上述
// router.get('/user', [mw, mw1], (req, res) => {
//     res.send('get请求')
// })
  • 注意事项
    • 多个全局中间件。请求到达服务器后会按照中间件定义的先后顺序执行
    • 一定要在路由之前注册中间件
    • 客户端发过来的请求,可以连续调用多个中间件进行处理
    • 执行完中间件的业务代码后,需要调用next函数(不要在next后再写额外代码)
    • 多个中间件之间共享同一份req和res。

3.3 中间件分类

  • 应用级别的中间件
    • 绑定到app实例上的中间件
    • 示例
    app.use((req, res, next) => { next() })
    app.get('/', mw, (req, res) => {})
    
  • 路由级别的中间件
    • 绑定到router实例上的中间件
    const mw = function(req, res,next) {
        console.log('这是中间件函数');
        next();
    }
    router.get('/user', mw, (req, res) => {
        res.send('get请求')
    })
    
  • 错误级别的中间件(必须注册在所有路由之后)
    • 专门捕获整个项目发生的异常情况
    app.get('/page', (req, res) => {
        throw new Error('服务器发生错误了');
        res.send('page')
    })
    <!-- 四个形参 -->
    app.use((err, req, res, next)=> {
        console.log('发生错误了:' + err.message)
        res.send('Error' + err.message)
    })
    
  • Express内置的中间件
    • express.static:快速托管静态资源的内置中间件
    • express.json:解析JSON格式的请求体数据
    • express.urlencoded:解析x-www-form-urlencoded格式的请求体数据
    • 请求体格式介绍
      • JSON
        • 需要传复杂数据
        • 使用现代前端框架
      • x-www-form-urlencoded
        • 提交传统HTML表单
        • 接口规范要求表单格式
        • 需要兼容IE等老旧浏览器
      • multipart/form-data
        • 表单包含文件上传控件
    // postman -> body -> json
    app.use(express.json())
    app.post('/user', (req, res) => {
        console.log(req.body); // 如果不配置express.json,获取undefined
        res.send('发送成功')
    })
    // postman -> body -> x-www-form-urlencoded
    app.use(express.urlencoded({ extended: false }))
    app.post('/user', (req, res) => {
        console.log(req.body); // 如果不配置urlencoded,获取undefined
        res.send('发送成功')
    })
    
  • 第三方的中间件
    • 如:body-parser中间件(现在使用express.urlencoded中间件)
      • 安装:npm install body-parser
      • 导入:require导入
      • 注册:app.use

3.4 自定义中间件

  • 手写express.urlencoded(body-parse)
    • 1.新建body-parse.js中间件
    const qs = require('querystring')
    function bodyParse(req, res, next){
        let rawData = '';
        req.on('data', chunk => {
            rawData += chunk
        })
        req.on('end', () => {
            // 把字符串转换成对象格式: name=zs&age=18 -> { name: 'zs', age: '18' }
            const body = qs.parse(rawData)
            req.body = body
            next();
        })
    }
    module.exports = bodyParse;
    
    • 2.导入及使用
    const bodyParse = require('./body-parse')
    app.use(bodyParse)
    app.post('/user', (req, res) => {
        console.log(req.body)
        res.send('发送成功')
    })
    

四、Express 写接口

4.1 接口编写

  • 示例
// 1.导入express
const express = require('express');
// 2.创建web服务器
const app = express();
const cors = require('cors')
app.use(cors())

app.use(express.json())
app.use(express.urlencoded({ extended: false }))

// 正常情况下需要路由模块化
app.post('/postApi', (req, res) => {
    res.send({
        status: 0,
        msg: 'post请求成功',
        data: req.body
    })
})

// 3.调用app.listen,启动服务器
app.listen(3000, () => {
    console.log('runing at http://127.0.0.1')
}).on('error', err => {
    console.log(err)
})

4.2 express解决跨域

  • cors
    • 定义:由一系列http响应头组成,这些响应头决定浏览器是否阻止JS代码跨域获取资源
    • 相关响应头:
      • Access-Control-Allow-Origin:允许访问该资源的外域URL
      • Access-Control-Allow-Headers:cors仅支持客户端发送的9个响应头(否则需声明)
      • Access-Control-Allow-Methods:cors仅支持get,post,head请求(否则需声明)
    • 请求分类:
      • 简单请求
      • 预检请求
    • 步骤
      • 安装:npm install cors
      • 导入:const cors = require(‘cors’)
      • 注册:app.use(cors())
    • 使用示例
    // 使用
    const cors = require('cors')
    app.use(cors())
    app.use(express.json())
    app.use(express.urlencoded({ extended: false }))
    app.post('/postApi', (req, res) => {
        res.send({
            status: 0,
            msg: 'post请求成功',
            data: req.body
        })
    })
    
    // 测试
    $('#btnPost').on('click', () => {
        $.ajax({
            type: 'post',
            url: 'http://127.0.0.1:3000/postApi',
            data: { name: 'zs', age: 18 },
            success: (res) => {
                console.log(res)
            }
        })
    })
    
  • JSONP:略

4.3 跨域介绍

  • 定义:由于浏览器同源策略:限制了不同源之间的资源访问
  • 解决本质:
    • 让请求’变同源’(代理方案)
    • 让服务器’允许’(cors方案)
  • 触发条件:
    • 协议不同:http://example.com -> https://example.com
    • 域名不同:https://example.com -> https://api.example.com
    • 端口不同:http://localhost:3000 -> http://localhost:5000
  • 常见场景:
    • AJAX请求:浏览器发送的跨域 HTTP 请求
    • 字体,图片:网页加载时需要跨域请求字体、图片等静态资源
    • WebSocket:客户端和服务器不在同一个源上
  • 解决方案:
    • cors:服务器添加响应头允许域名访问(如:Access-Control-Allow-Origin: ‘*’)
    • jsonp:<script>可以跨域加载js文件
      • 1.前端定义回调函数:function handleData(data) { // 处理数据 }
      • 2.动态创建<script> 标签
      const script = document.createElement('script');  
      script.src = 'http://api.com/data?callback=handleData'; // 挂载回调函数名  
      document.body.appendChild(script);
      
      • 3.后端返回函数调用
      handleData({ name: "小明", age: 25 });
      
    • 开发环境正向代理:
      • 过程:/api/data -> http://api.example.com/data
      server: {
          proxy: {
              '/api': {
                  target: 'http://api.example.com',
                  changeOrigin: true, // 修改请求头中的Host
                  rewrite: path => path.replace(/^\/api/, '') // 去掉/api前缀
              }
          }
      }
      
    • Nginx反向代理:
      • 过程:https://your-domain.com/api/data -> 通过nginx -> http://api.example.com/data
          # API代理规则
          location /api/ {
              proxy_pass http://api.example.com/; # 末尾斜杠很重要!
              proxy_set_header Host $host;
          }
      
    • WebSockt:建立持久化跨域通道
×

喜欢就点赞,疼爱就打赏