Express

1. 初识Express

1.1 Expres简介

  1. 什么是 Express

    • 概念:Express 是基于 Node.js 平台,快速、开放、极简的 Web 开发框架。
    • 本质:第三方包,提供了快速创建 Web 服务器的便捷方法。
  2. Express 的作用
    对于前端来说,最常见的两种服务器,分别是:

    • Web 网站服务器:专门对外提供web网专资源的服务器。
    • API接口服务器:专门对外提供API接口的服务器。

    使用 Express,可以方便、快速创建Web网站的服务器或API接口的服务器。

1.2 Express的基本使用

  1. 运行 npm i express@4.17.1 命令安装 express

  2. 创建基本的Web服务器

    // 1. 导入 express  
    const express = require('express');
    // 2. 创建 Web 服务器实例对象  
    const app = express();
    // 3. 定义端口号  
    const port = 3000;
    
    // 4. 调用app.listen(端口号,启动成功后的回调函数) 方法,启动服务器
    app.listen(port, () => {
        console.log('Express server running at http://127.0.0.1:3000');
    })
    
  3. 监听GET请求语法格式

    /*
     * @alias app.get(url, callback)
     * @param {string} 客户端请求的URL地址
     * @param {callback} 响应客户端请求的处理函数
     */
    app.get('url', (req, res) => {
        // req: 请求对象(包含了与请求相关的属性与方法)
        // res: 相应对象(包含了与响应相关的属性与方法)
        // 处理函数体
    })
    
  4. 监听POST请求语法格式

    /*
     * @alias app.post(url, callback)
     * @param {string} 客户端请求的URL地址
     * @param {callback} 响应客户端请求的处理函数
     */
    app.post('url', (req, res) => {
        // req: 请求对象(包含了与请求相关的属性与方法)
        // res: 相应对象(包含了与响应相关的属性与方法)
        // 处理函数体
    })
    
  5. 获取URL中携带的查询参数
    通过 req.query 对象,可以访问到客户端通过查询字符串的形式发送到服务器的参数:

    app.get('/search', (req, res) => {
       // req.query 默认是一个空对象
       // 当客户端的请求URL为:/search?q=taobao
       console.log(req.query); // 输出: { q: 'taobao' }
       console.log(req.query.q); // 输出: taobao
    })
    
  6. 获取表单中的数据
    通过 req.body 对象,可以获取表单提交过来的数据:

    // 如果需要使用 req.body , 需要在使用前使用解析表单的中间件(下面会详细说)
    app.use(express.urlencoded({ extended: true }));
    // 如果使用上述代码,req.body 的值为 undefined  
    app.post('/reg', (req, res) => {
       console.log(req.body);
    })
    
  7. 获取URL中的动态参数
    通过 req.params 对象,可以访问到URL中通过 : 匹配到的动态参数:

    // :id 表示 id 这个参数是动态参数  
    app.get('/user/:id', (req, res) => {
       // req.params 默认是一个空对象
       // 客户端请求URL为: /user/34
       console.log(req.params); // 输出: { id: '34' }
    })
    
  8. 响应客户端请求
    通过 res.send() 方法,可以把数据和信息发送给客户端。

    app.get('/', (req, res) => {
       // 向客户端发送 JSON 对象
       res.send({
          name: 'xao',
          age: 18,
          sex: '男',
       });
    })
    
    app.post('/user', (req, res) => {
       // 向客户端发送文本内容
       res.send('POST请求成功');
    })
    

1.3 托管静态资源

  • 静态资源目录树结构
    |--public
    |--|--index.html
    |--|--login.html
    |--|--register.html
    
  1. express.static()
    通过使用 express.static() 方法,可以快速创建一个静态资源服务器。

    app.use(express.static('./public'));
    // 通过上述代码,客户端就可以访问public目录下的静态资源
    // htpp://127.0.0.1:3000/index.html
    // htpp://127.0.0.1:3000/login.html
    // htpp://127.0.0.1:3000/register.html
    
  2. 托管多个静态资源目录
    如果要托管多个静态资源目录,请多次调用express.static() 方法

    app.use(express.static('./public'));
    app.use(express.static('./files'));
    

    客户端访问静态资源时,express.static() 函数会根据目录的添加顺序查找所需的文件。

  3. 挂载路径前缀
    如果希望在托管的静态资源访问路径之前,挂载路径前缀,则可以使用如下的方式:

    app.use('/public',express.static('./public'));
    // 添加上述代码后,客户端访问资源时必须要带有 /public 前缀地址来访问public目录中的静态资源
    // htpp://127.0.0.1:3000/public/index.html
    // htpp://127.0.0.1:3000/public/login.html
    // htpp://127.0.0.1:3000/public/register.html
    

2. Express 路由

2.1 路由的概念

  1. 从广义上来讲,路由就是映射关系。

  2. express 中的路由

    • 在 express 中,路由指的是 客户端的请求服务器处理函数 之间的映射关系。
    • express 中的路由分 3 部分组成,分别是 请求的类型请求的 URL 地址处理函数
  3. 具体代码

    // 匹配GET请求,且请求 URL 地址为 /index.html  
    app.get('/index.html', (req, res) => {
       res.send('hello world !');
    })
    
    // 匹配POST请求,且请求 URL 地址为 /login.html  
    app.post('/login.html', (req, res) => {
       res.send('登录成功');
    })
    
  4. express路由匹配过程

    • 每当一个请求到达服务器之后,需要先经过路由的匹配,只有匹配成功之后,才会调用对应的处理函数。
    • 在匹配时,会按照路由定义的顺序进行匹配,如果请求类型和请求的 URL 同时匹配成功,则 Express 会将这次请求,转交给对应的 function 函数进行处理。

2.2 路由的使用

  1. 简单使用方法
    将路由挂载到服务器实例对象上

    // 导入 express 
    const express = require('express')
    // 创建 web 服务器实例对象
    const app = express()
    // 定义端口号
    const port = 3000
    
    // 挂载路由
    app.get('/', (req, res) => {
       res.send('GET request to the homepage')
    })
    app.post('/', function (req, res) {
       res.send('POST request to the homepage')
    })
    
    // 启动服务器
    app.listen(port, () => {
       console.log(`Express server running at http://127.0.0.1:3000`)
    })
    
  2. 模块化路由
    为了方便对路由进行模块化的管理,Express 不建议将路由直接挂载到 app 上,而是推荐将路由抽离为单独的模块。

    1. 将路由抽离为单独模块的步骤如下:

      1. 创建路由模块对应js文件
      2. 调用 express.Router() 函数创建路由对象
      3. 定义路由的具体映射关系
      4. 使用 module.exports 向外共享路由对象
      5. 使用 app.use() 函数注册路由模块
    2. 代码如下

      • 创建路由模块
        // 导入 express 
        const express = require('express');
        
        //创建路由对象 
        const router = express.Router();
        
        // 定义路由的映射关系
        router.get('/', (req, res) => {
          res.send('GET request to the homepage')
        })
        router.post('/', function (req, res) {
          res.send('POST request to the homepage')
        })
        
        // 向外共享路由对象
        module.exports = router;
        
      • 注册路由模块
        // 导入路由模块
        const router = require('./router.js');
        // 使用 app.use() 注册路由模块  
        app.use(router);
        
      • 为路由模块添加路径前缀
        // 使用 app.use() 注册路由模块,并统一添加访问路径前缀  
        app.use('/index', router);
        

3. Express 中间件

3.1 中间件的概念

  1. 中间件(Middleware ),特指业务流程的 中间处理环节

  2. express 中间件的调用流程

    1. 当一个请求到达服务器之后,可以连续调用多个中间件,从而对这次请求进行预处理。
    2. 多个中间件之间共享 req 和 res 对象。
    3. 当处理完毕后,响应客户端的请求。

3.2 中间件的类型

  1. 应用层中间件
    通过 app.use() 和 app.METHOD() 函数,绑定到 express() 实例对象上的中间件,叫做应用层中间件。

    const app = express();
    // 每次收到请求,都会执行该中间件(全局中间件)
    app.use((req, res, next) => {
       console.log('hello express !');
       next();
       // next() 方法必须被调用,以便进入下一个中间件。否则,请求将被挂起。
       // 调用 next() 方法之后,不要再书写任何代码。(一般最后调用 next() 方法)
    })
    // 该中间件只会在客户端请求 /api 路径时,被执行
    app.use('/api', (req, res, next) => {
       console.log('api');
       next();
    })
    // 该中间件只会在客户端发起 GET 请求,且请求 /public 路径时被执行
    app.get('/public', (req, res, next) => {
       console.log('public');
       next();
    })
    
  2. 路由级中间件
    路由器级中间件与应用层中间件的工作方式相同,只不过它绑定到的实例 express.Router() 上。

    const userRouter = express.Router();
    // 该中间件只会在客户端请求 userRouter 模块时被执行
    userRouter.use((req, res, next) => {
       console.log('success');
       next();
    })
    // 该中间件只会在客户端请求 userRouter 模块,且请求路径为 /userinfo 时被执行
    userRouter.use('/userinfo', (req, res, next) => {
       console.log('userinfo');
       next();
    })
    // 该中间件只会在客户端请求 userRouter 模块,发起 POST 请求,且路径为 /userinfo/update 时被执行
    userRouter.post('/userinfo/update', (req, res, next) => {
       console.log('update userinfo');
       next();
    })
    
  3. 错误处理中间件

    • 错误中间件必须带有4个参数,(err, req, res, next)
    • 错误中间件无需调用 next() 方法,但参数必须传;否则,会被当做其他类型的中间件执行。
    • 错误中间件必须在所有路由之后调用
    app.use((err, req, res, next) => {
       if(req.url == '/api/info') {
          return res.status(404).send('404 页面不存在!');
       }
       res.status(500).send('服务器内部错误');
    })
    
  4. 内置中间件

    • express.static 快速托管静态资源的内置中间件,例如: HTML 文件、图片、CSS 样式等(无兼容性)。
    • express.json 解析 JSON 格式的请求体数据(有兼容性,4.16.0+ 中可用)。
    • express.urlencoded 解析 URL-encoded 格式的请求体数据(有兼容性,4.16.0+ 中可用)。
  5. 第三方中间件(与包类似)

    • 非express官方内置的中间件,而是有第三方开发出来的express中间件,叫做第三方中间件。
    • 使用第三方中间件时,需要使用 npm i 名称 下载第三方中间件,使用 require() 导入,并调用 app.use() 挂载第三方中间件。

3.3 自定义中间件

// 新建一个js文件
// 自定义模块可以使用 node.js 内置模块 或 第三方模块  

// 定义中间件
const myMiddle = (req, res, next) => {
   // 要执行的代码...
   next();
}

// 向外共享myMiddle
module.exports = myMiddle;

4. 使用过 express 搭建API接口服务器

4.1 使用过 express 搭建API接口

  1. 搭建基本服务器

    // 导入 express
    const express = require('express');
    // 创建服务器实例对象
    const app = express();
    // 定义端口号
    const port = 3000;
    
    // 导入路由模块
    const apiRouter = require('./apiRouter');
    
    // 将路由模块注册到服务器上
    app.use('/api', apiRouter);
    
    app.listen(port, () => console.log(`Example server running at http://127.0.0.1:3000`));
    
  2. apiRouter 模块

    // 导入 express
    const express = require('express');
    // 创建一个新的路由对象
    const router = express.Router();
    
    // 实现路由的映射
    router.get('/get', (req, res) => {
       const query = req.query;
       res.send({
          status: 200, // 响应状态码:200
          msg: 'GET请求成功', // 响应状态描述
          data: query, // 响应给客户端的数据
       });
    });
    
    router.post('/post', (req, res) => {
       const query = req.query;
       res.send({
          status: 200, // 响应状态码:200
          msg: 'POST请求成功', // 响应状态描述
          data: query, // 响应给客户端的数据
       })
    })
    // 向外共享路由模块
    module.exports = router;
    

4.2 跨域资源共享

  1. CORS(Cross-Origin Resource Sharing)跨域资源共享

    1. 同源:客户端请求的URL地址与服务器的ULR地址的协议域名端口号都相同即为同源,三者有一者不同为跨域。
    2. 由于浏览器同源策略的存在,默认阻止跨域获取资源;但服务器可以通过设置响应头,允许跨域请求访问资源。
    3. 但CORS 在浏览器中有兼容性,只支持 XMLHttpRequest Level2 的浏览器,才能正常开启 CORS (例如:IE10+、Chrome4+、FireFox3.5+)。
  2. CORS 跨域资源实现方式
    通过指定响应头 Access-Control-Allow-Origin 字段对应的值来指定允许访问资源的外域 URL。

    // 只允许来自 http://127.0.0.1:5500 的跨域访问资源请求
    res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:5500');
    // 值为 '*' : 表示允许来自任何域的请求
    res.setHeader('Access-Control-Allow-Origin', '*');
    
  3. CORS 默认支持9个请求头

    Accept
    Accept-Language
    Content-Language
    DPR
    Downlink
    Save-Data
    Viewport-Width
    Width 
    Content-Type (值仅限于 text/plain、multipart/form-data、application/x-www-form-urlencoded 三者之一)
    

    如果需要支持其他的类型的请求头,可以通过 Access-Control-Allow-Headers 字段对额外的请求进行声明。

    // 允许客户端额外向服务器发送 X-Custom-Header 请求头
    res.setHeader('Access-Control-Allow-Headers', 'X-Custom-Header');
    // 可以同时设置多个请求头,多个请求头之间用英文逗号分隔
    res.setHeader('Access-Control-Allow-Headers','Content-Type,X-Custom-Header');
    
  4. CORS 默认情况下仅支持客户端发起的简单请求(GET、POST、HEAD)。
    如果服务器想要允许客户端通过非简单请求(预检请求)来请求服务端的资源,可以通过 Access-Control-Alow-Methods 字段来设置客户端可以使用的请求方式。

    // 允许客户端使用 GET, POST, HEAD, DELETE 方式请求资源
    res.setHeader('Access-Control-Alow-Methods', 'GET, POST, HEAD, DELETE');
    // 允许客户端使用所有的HTTP请求方式
    res.setHeader('Access-Control-Alow-Methods', '*');
    
  5. 简单请求

    • 请求方式为 GET、POST、HEAD 三者之一。
    • 客户端请求头部无自定义头部字段。
    • 客户端只会向服务端发送一次请求。
  6. 预检请求

    • 请求方式为 GET、POST、HEAD 之外的请求类型。
    • 请求头部包含自定义头部字段。
    • 向服务器发送 application/json 格式的数据。
    • 客户端会向服务器发送两次请求。
      1. 在浏览器与服务器正式通信之前,浏览器会先发送 OPTION 请求进行预检,以获知服务器是否允许该实际请求。
      2. 服务器成功响应OPTION请求(预检请求)后,才会发送真正的请求,并且携带真实数据。

4.3 跨域解决方案

  1. CORS(主流方案,只需要在后端配置,推荐使用)
  2. JSONP(兼容性好,需要前后端配合,但只支持GET请求)

4.4 使用 cors 中间件解决跨域问题

cors 是 express 的一个第三方中间件,通过安装和配置中间件,可以方便解决跨域问题。

  • 安装: npm i cors
  • 导入: const cors = require('cors')
  • 全局配置: app.use(cors())

4.5 使用 jsonp 解决跨域问题

  • 服务器端代码
    app.get('/api/jsonp', (req, res) => {
       // 1. 得到函数的名称
       const funName = req.query.callback
       // 2. 定义要发送到客户端的数据对象
       const data = { name: 'zs', age: 22 }
       // 3. 拼接出一个函数的调用
       const scriptStr = `${funName}(${JSON.stringify(data)})`
       // 4. 把拼接的字符串,响应给客户端
       res.send(scriptStr)
    })
    
  • 客户端代码
    $.ajax({
       type: 'GET',
       url: 'http://127.0.0.1/api/jsonp',
       dataType: 'jsonp',
       success: function (res) {
          console.log(res);
       }
    })
    

PS: 如果本文对你有所帮助,请点个赞吧!

原文地址:https://www.cnblogs.com/discourage/p/13858080.html