• Node.js的Express简介


    环境

    • Ubuntu 22.04
    • Node.js 16.15.1
    • VSCode 1.69.2
    • Firefox 103.0

    概述

    Express官网对Express的定义是:基于Node.js平台,快速、开放、极简的Web开发框架。

    Express是npm上的第三方包,作用和Node.js内置的 http 模块类似,用来创建Web服务器。

    安装方法为: npm i express

    简单示例

    创建文件 0727_9.js 如下:

    const express = require('express');
    
    const app = express();
    
    app.get('/index.html', (req, res) => {
        res.send('Success!');
    });
    
    app.listen(8080, () => {
        console.log('server started');
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    运行程序,访问服务器:

    在这里插入图片描述

    可以用 req.query 获取查询参数(比如 ?name=Tom&age=20 ):

    app.get('/a.html', (req, res) => {
        res.send(req.query);
    });
    
    • 1
    • 2
    • 3

    效果如下:

    在这里插入图片描述

    可以用 req.params 获取请求的路径参数:

    app.get('/:xxx/b.html', (req, res) => {
        res.send(req.params);
    });
    
    • 1
    • 2
    • 3

    效果如下:

    在这里插入图片描述

    托管静态资源

    使用 express.static() 方法,可以很方便的创建一个静态资源服务器。

    我们之前通过把MarkDown文档转换为HTML文档,生成了 preview 目录,其中包含了 test.html 文件,和一些css以及js文件。如下:

    在这里插入图片描述

    创建文件 0727_10.js 如下:

    const express = require('express');
    
    const app = express();
    
    app.use(express.static('preview'));
    
    app.listen(8080, () => {
        console.log('server started');
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    效果如下:

    在这里插入图片描述

    也可以定制化访问的URL路径,例如:

    app.use('/bbb', express.static('preview'));
    
    • 1

    则访问时,也需要加上 /bbb 前缀,即: http://localhost:8080/bbb/test.html

    Express路由

    前面的例子里,我们使用了多个 app.get() 方法,对应不同的请求处理。事实上Express不建议将路由直接挂载到app上,而是推荐将路由抽离为单独的模块。使用 express.Router() 方法创建路由对象,通过 module.exports() 导出路由对象,在外面使用 app.use() 方法注册路由模块。

    创建文件 0727_11.js

    const express = require('express');
    
    const router = express.Router();
    
    router.get('/users', (req, res) => {
        res.send('get users');
    });
    
    router.get('/books', (req, res) => {
        res.send('get books');
    });
    
    module.exports = router;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    创建文件 0727_12.js

    const express = require('express');
    
    const myRouter = require('./0727_11.js');
    
    const app = express();
    
    app.use(myRouter);
    
    app.listen(8080, () => {
        console.log('server started');
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    效果如下:

    在这里插入图片描述

    在这里插入图片描述

    同样,也可以定制化访问的URL路径,例如:

    app.use('/bbb', myRouter);
    
    • 1

    则访问时,也需要加上 /bbb 前缀,即: http://localhost:8080/bbb/users

    Express中间件

    可以在处理请求之前,先做一个或者多个预处理。

    注意:一定要在路由之前注册中间件。

    全局中间件

    全局中间件适用于所有请求。

    用法为:

    app.use((req, res, next) => {
        ......
        next();
    });
    
    • 1
    • 2
    • 3
    • 4

    创建文件 0727_13.js

    const express = require('express');
    
    const app = express();
    
    app.use((req, res, next) => {
        console.log(req.url + ': 1st middleware');
        next();
    });
    
    app.use((req, res, next) => {
        console.log(req.url + ': 2nd middleware');
        next();
    });
    
    app.use((req, res, next) => {
        console.log(req.url + ': 3rd middleware');
        next();
    });
    
    app.get('/users', (req, res) => {
        res.send('get users');
    });
    
    app.get('/books', (req, res) => {
        res.send('get books');
    });
    
    app.listen(8080, () => {
        console.log('server started');
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    当访问 http://localhost:8080/users 时,输出如下:

    /users: 1st middleware
    /users: 2nd middleware
    /users: 3rd middleware
    
    • 1
    • 2
    • 3

    当访问 http://localhost:8080/books 时,输出如下:

    /books: 1st middleware
    /books: 2nd middleware
    /books: 3rd middleware
    
    • 1
    • 2
    • 3

    可见:

    • 全局中间件适用于所有请求;
    • 可定义多个全局中间件,按其定义顺序执行;

    局部中间件

    全局中间件针对所有的请求,如果想要只针对指定的请求,可以使用局部中间件。

    创建文件 0727_13.js

    const express = require('express');
    
    const app = express();
    
    const mw1 = (req, res, next) => {
        console.log(req.url + ': 1st middleware');
        next();
    };
    
    const mw2 = (req, res, next) => {
        console.log(req.url + ': 2nd middleware');
        next();
    };
    
    const mw3 = (req, res, next) => {
        console.log(req.url + ': 3rd middleware');
        next();
    };
    
    app.get('/users', [mw1, mw2], (req, res) => {
        res.send('get users');
    });
    
    app.get('/books', [mw2, mw3], (req, res) => {
        res.send('get books');
    });
    
    app.listen(8080, () => {
        console.log('server started');
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    本例中,给 /users 请求绑定了 mw1mw2 这两个中间件,给 /books 请求绑定了 mw2mw3 这两个中间件。

    当访问 http://localhost:8080/users 时,输出如下:

    /users: 1st middleware
    /users: 2nd middleware
    
    • 1
    • 2

    当访问 http://localhost:8080/books 时,输出如下:

    /books: 2nd middleware
    /books: 3rd middleware
    
    • 1
    • 2

    可见,只有绑定的中间件才会生效。

    中间件的分类

    • 应用级别的中间件
    • 路由级别的中间件
    • 错误级别的中间件
    • Express内置的中间件
    • 第三方中间件

    应用级别的中间件

    通过 app.use()app.get()app.post() 绑定到 app 实例上的中间件。

    前面的例子,都是应用级别的中间件。

    路由级别的中间件

    绑定到 express.Router() 上的中间件,其用法和应用级别中间件类似。

    创建文件 0727_15.js 如下:

    const express = require('express')
    
    const app = express();
    
    const router = express.Router();
    
    router.use((req, res, next) => {
        console.log(req.url + ': 1st router');
        next();
    });
    
    router.use((req, res, next) => {
        console.log(req.url + ': 2nd router');
        next();
    });
    
    router.get('/users', (req, res) => {
        res.send('get users');
    });
    
    router.get('/books', (req, res) => {
        res.send('get books');
    });
    
    app.use(router);
    
    app.listen(8080, () => {
        console.log('server started');
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    当访问 http://localhost:8080/users 时,输出如下:

    /users: 1st router
    /users: 2nd router
    
    • 1
    • 2

    当访问 http://localhost:8080/books 时,输出如下:

    /books: 1st router
    /books: 2nd router
    
    • 1
    • 2

    错误级别的中间件

    注意:需要注册在所有路由之后,这是和其它中间件不同的。

    创建文件 0727_16.js 如下:

    const express = require('express');
    
    const app = express();
    
    app.get('/', (req, res) => {
        console.log('get it');
        throw new Error('Something is wrong!');
        res.send('Can you get it?');
    });
    
    app.use((err, req, res, next) => {
        console.log('Error occurred');
        console.log(err.message);
    
        res.send('Sorry but an error occurred');
    });
    
    app.listen(8080, () => {
        console.log('server started');
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    运行程序,访问 http://localhost:8080 ,如下:

    在这里插入图片描述

    输出如下:

    get it
    Error occurred
    Something is wrong!
    
    • 1
    • 2
    • 3

    Express 内置的中间件

    比如 express.static() ,在前面提到过。

    第三方的中间件

    需要显式安装的三方包。

    跨域请求

    创建文件 0727_17.js 如下:

    const express = require('express');
    //const cors = require('cors');
    
    const app = express();
    
    //app.use(cors());
    
    app.get('/users', (req, res) => {
        res.send('get users');
    });
    
    app.listen(8080, () => {
        console.log('server started');
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    效果如下:

    在这里插入图片描述

    创建文件 0727_17.html 如下:

    DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Documenttitle>
        <script src="lib/jquery-3.6.0.min.js">script>
    head>
    <body>
        <button id="btn1">OKbutton>
        <script>
            $('#btn1').on('click', function () {
                $.get('http://localhost:8080/users', function (res) {
                    console.log(res);
                });
            });
        script>
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    点击 OK 按钮会报错:

    在这里插入图片描述

    报错原因:这是一个跨域请求,因为二者的协议不同( file VS. http )。

    解决办法有 CORSJSONP

    CORS

    首先要安装 cors

    ➜  temp0727 npm i cors
    
    added 2 packages, and audited 60 packages in 2s
    
    7 packages are looking for funding
      run `npm fund` for details
    
    found 0 vulnerabilities
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    0727_17.js 文件中添加两行代码(参考注释的代码):

    const cors = require('cors');
    
    app.use(cors());
    
    • 1
    • 2
    • 3

    注意要放在 app.get() 前面。

    重启服务,现在点击 OK 按钮就不报错了:

    在这里插入图片描述

    查看Response Headers,如下:

    在这里插入图片描述

    可见,多了 Access-Control-Allow-Origin: *

    注意:浏览器貌似有缓存,如果此时删掉那两行代码,然后重启服务,刷新浏览器,点击 OK 按钮,仍然能成功。只有关闭并重新打开浏览器,点击 OK 按钮才会失败。

    客户端在请求cors接口时,可以将cors的请求分为两大类:

    • 简单请求
    • 预检请求

    同时满足以下两大条件的请求,就属于简单请求:

    • 请求方式: GET 或者 POST 或者 HEAD
    • 头部信息:
      • 无自定义头部字段
      • Accept
      • Accept-Language
      • Content-Language
      • DPR
      • Downlink
      • Save-Data
      • Viewport-Width
      • Width
      • Content-Type(只有三个值application/x-www-form-urlencoded、multipart/form-data、text/plain)

    其它请求则属于预检请求。

    对于预检请求,浏览器会先发送一个 OPTIONS 请求进行预检,以获知服务器是否允许该实际请求,服务器成功响应预检请求后,才会发送真正的请求。

    创建文件0727_18.js 如下:

    const express = require('express');
    const cors = require('cors');
    
    const app = express();
    
    app.use(cors());
    
    app.delete('/users', (req, res) => {
        res.send('delete users');
    });
    
    app.listen(8080, () => {
        console.log('server started');
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    创建文件 0727_18.html 如下:

    DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Documenttitle>
        <script src="lib/jquery-3.6.0.min.js">script>
    head>
    <body>
        <button id="btn1">OKbutton>
        <script>
            $('#btn1').on('click', function () {
                $.ajax({
                    type: 'DELETE',
                    url: 'http://localhost:8080/users',
                    success: function (res) {
                        console.log(res);
                    }
                });
            });
        script>
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    效果如下:

    在这里插入图片描述

    可见,对于 DELETE 请求,浏览器先发送了一个 OPTIONS 请求,然后才发送真正的 DELETE 请求。

    JSONP

    创建文件 0727_19.js 如下:

    const express = require('express');
    
    const app = express();
    
    app.get('/jsonp', (req, res) => {
        const funcName = req.query.callback;
        const data = {name: 'Tom', age: 20};
        const str = `${funcName}(${JSON.stringify(data)})`;
        res.send(str);
    })
    
    app.listen(8080, () => {
        console.log('server started');
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    创建文件 0727_19.html 如下:

    DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Documenttitle>
        <script src="lib/jquery-3.6.0.min.js">script>
    head>
    <body>
        <button id="btn1">OKbutton>
        <script>
            $('#btn1').on('click', function () {
                $.ajax({
                    type: 'GET',
                    url: 'http://localhost:8080/jsonp',
                    dataType: 'jsonp',
                    success: function (res) {
                        console.log(res);
                    }
                });
            });
        script>
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    效果如下:

    在这里插入图片描述

    在这里插入图片描述

    参考

    • http://expressjs.com
    • https://www.expressjs.com.cn (中文)
  • 相关阅读:
    CS224W1.1——图机器学习介绍
    第一个ARM程序裸板点灯
    前端HTML5 +CSS3 5.CSS布局 4 浮动
    【实战技能】非技术背景经理的技术管理
    几点建议,帮您做好线上价格监测
    Docker实战-部署GPE微服务的监控体系(二)
    这些客厅背景墙,你都收藏了吗?福州中宅装饰,福州装修
    Matplotlib实现Label及Title都在下方的最佳姿势
    云原生强大且灵活的持续集成CI开源框架Tekton实战-上
    免费开放商用!Stability AI推轻量级AI绘画利器 Stable Diffusion 3.5 Medium模型
  • 原文地址:https://blog.csdn.net/duke_ding2/article/details/126022253