• 【docker desktop】创建node:18 server


    1、创建node18容器

    docker run --rm -it -v "D:/AIHUB_workSpace/USC-courses/EE547":/usr/src/app -p 3000:3000 node:18 /bin/bash
    
    • 1

    如果没有会自动下载node:18的image,然后自动进入container内部,退出会自动删除container,该image大小为990.67MB

    注:之后使用npm install也在/usr/src/app下进行,如果在进入container时的默认根目录进行,可能会出现如Tracker "idealTree" already exists的错误

    2、查看版本

    root@f2fd36a194ca:/# node -v
    v18.9.0
    root@f2fd36a194ca:/# npm -v
    8.19.1
    
    • 1
    • 2
    • 3
    • 4

    3、创建index.js文件
    因为本地文件夹已经mount到了container内部,在windows该文件夹下直接添加文件便能在container内部看到

    'use strict';
    const http = require('http');
    const server = http.createServer(function (req, res) {
     if (req.url == '/') {
     res.writeHead(200, { 'Content-Type': 'text/html' });
     res.write('Hello, node.js!');
     res.end();
     }
    });
    server.listen(3000);
    console.log('Server started, port 3000');
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    此时/usr/src/app下应该有index.js文件
    到/usr/src/app启动该server

    root@f2fd36a194ca:/usr/src/app# node index.js
    Server started, port 3000
    
    • 1
    • 2

    4、请求该端口
    因为映射了端口3000到container内部的3000
    因此可以直接打开浏览器

    http://127.0.0.1:3000/
    
    • 1

    便能看到Hello, node.js!的返回结果

    或者如果在Windows安装了curl,可以打开cmd,便能有如下结果

    C:\Users\ASUS>curl http://127.0.0.1:3000/
    Hello, node.js!
    
    • 1
    • 2

    5、使用nodemon

    当用该命令启动index.js时,如果nodemon检测到index.js文件有改变,会自动重新启动服务,便于快速开发

    在container内部安装

    npm install nodemon
    
    • 1

    如果这行安装不了,用

    npm install -g nodemon
    
    • 1

    因为我们使用nodemon一般是在命令行使用,所以加上-g安装的nodemon才提供在命令行中使用该库的工具,否则就会提示找不到nodemon。如果我们只需要在程序中调用该包,则不用加-g即可

    如果不小心关掉了cmd界面用该命令重新进入container

    docker exec -it f2fd36a194cad7c294edbcbe04f5ae2bce003cb52c8d55b30f320ead6367af0c /bin/bash
    
    • 1

    现在用nodemon启动index.js

    nodemon index.js
    
    • 1

    http://127.0.0.1:3000/secret访问服务器,会发现没有反应,也不会自动结束,而是处于等待状态

    现在进化一下index.js如下,使之可以返回404

    'use strict';
    const http = require('http');
    const PORT = 3000;
    const server = http.createServer((req, res) => {
     if (req.url == '/') {
     res.writeHead(200, { 'Content-Type': 'text/html' });
     res.write('Hello, node.js!');
     res.end();
     } else {
     res.writeHead(404);
     res.end();
     }
    });
    server.listen(PORT);
    console.log(`Server started, port ${PORT}`);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    当我们在Windows端修改了该文件,nodemon会自动检测到文件改变,然后自动重新启动

    现在访问不存在的路径会自动返回404

    注:自动启动功能在Windows10上结合docker desktop,并没有测试成功,仍需手动重启,可能有一些兼容性原因; 一个StackOverflow上的解决方法是用nodemon --legacy-watch index.js启动,--legacy-watch会将nodemon听(listen)文件系统事件的行为变成轮询(poll),这样理论上会慢一点,但可以保证能够检测到文件的改变并自动重启

    6、一个更好的index.js

    'use strict';
    const http = require('http');
    
    const PORT = 3000;
    
    const server = http.createServer((req, res) => {
        let respBody = '';
        let contentType = 'text/html';
        let httpCode = 404;
    
        try {
            switch (req.method.toLowerCase()) {
                case 'get':
                    // can add logs to see elements
                    console.error(req.url)
                    const urlPath = req.url.split('/');
    
                    if (req.url === '/') { 
                        httpCode = 200;
                        respBody = 'Welcome home';
                    } else if (urlPath.length === 2 && urlPath[1] === 'greet') {
                        httpCode = 200;
                        respBody = 'Hello, node.js!';
                    } else if (urlPath.length === 3 && urlPath[1] === 'greet' && urlPath[2].match(/^a-z+$/)) {
                        httpCode = 200;
                        respBody = `Hello ${urlPath[2]}, node.js!`;
                    }
                    break;
                case 'post':
                    // add me
                    break;
            }
        } catch (err) {
            respBody = `Error: ${err.message}`;
            httpCode = 500;
        }
    
        res.writeHead(httpCode, { 'Content-Type': contentType });
        res.write(respBody);
        res.end();
    });
    
    server.listen(PORT);
    
    console.log(`Server started, port ${PORT}`);
    
    • 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
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    调试,-v可以输出完整的接受到的包信息

    curl -v http://127.0.0.1:3000/greet
    
    • 1

    7、一个神奇的报错
    [nodemon] app crashed - waiting for file changes before starting...

    其实是因为我的js里有个语法错误,一个括号没有闭合而已,和文件有没有改变没有关系

    8、使用url包,自动解析url,更易于应对更加复杂的url

    'use strict';
    const http = require('http');
    
    const PORT = 3000;
    
    // const qs = require('querystring');
    const url = require('url');
    
    const server = http.createServer((req, res) => {
        let respBody = '';
        let contentType = 'text/html';
        let httpCode = 404;
    
        console.error(req.url)
        const reqUrl = url.parse(req.url, true);
        console.error(reqUrl)
        const urlPath = reqUrl.pathname.split('/');
    
        console.error(urlPath)
    
        if (urlPath.length === 2 && urlPath[1] === 'greet') {
            httpCode = 200;
            respBody = reqUrl.query.name ? `Hello ${reqUrl.query.name}, node.js!` :
            'Hello, node.js!';
        } else { }
    
        res.writeHead(httpCode, { 'Content-Type': contentType });
        res.write(respBody);
        res.end();
    })
    
    server.listen(PORT);
    // 这行代码将被执行,因为js的异步机制,而在python中则不会执行到这
    console.log(`Server started, port ${PORT}`);
    
    • 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
    • 31
    • 32
    • 33
    • 34

    9、express中间件

    我猜测express是基于html包做了封装,或者实现了类似html包的功能,也能提供服务,监听端口,并进行路由,以及其他更强大的功能

    安装express,因为不需要在terminal使用,因此不用加-g(globally),直接locally安装即可

    npm install express
    
    • 1

    index.js文件

    'use strict';
    const express = require('express');
    const app = express();
    
    app.get('/greet', (req, res) => {
        res.writeHead(200, { 'Content-Type': 'text/html' });
        res.write('Hello, express!');
        res.end();
    });
    
    app.listen(3000);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    可见这里我们没有处理除了/greet以外的路径,但express会自动处理此问题,返回404,不会像html包那样需要自己处理

    10、静态内容

    创建public文件夹,放入index.html

    <html>
     <body>
     <p>
     Hello, static page!
     p>
     body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在index.js添加app.use那一句

    'use strict';
    const express = require('express');
    const app = express();
    
    app.use(express.static('public'));
    
    app.get('/greet', (req, res) => {
        res.writeHead(200, { 'Content-Type': 'text/html' });
        res.write('Hello, express!');
        res.end();
    });
    
    app.listen(3000);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    此时访问直接访问http://127.0.0.1:3000时就不会报错,会自动返回该index.html

    添加图片:在public下创建image文件夹,并添加一张图

    在index.html中使用

    <html>
     <body>
     <p>
     Hello, static page!
     p>
     <div id="mascot">
     <a href="https://www.google.com/search?q=silly+cats">
     <img src="/image/deeplearning.jpg" />
     a>
     div>
     body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    11、url传参

    'use strict';
    const express = require('express');
    const app = express();
    
    app.use(express.static('public'));
    
    app.get('/greet', (req, res) => {
     res.writeHead(200, { 'Content-Type': 'text/html' });
     res.write(`Hello, ${req.query.first_name} ${req.query.last_name}`);
     res.end();
    });
    app.get('/greet/:firstName/:lastName', (req, res) => {
     res.writeHead(200, { 'Content-Type': 'text/html' });
     res.write(`Hello, ${req.params.firstName} ${req.params.lastName}`);
     res.end();
    });
    
    app.listen(3000);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    比如

    http://127.0.0.1:3000/greet?first_name=1&last_name=xixi
    得到Hello, 1 xixi
    
    http://127.0.0.1:3000/greet/xixi/haha
    得到Hello, xixi haha
    
    • 1
    • 2
    • 3
    • 4
    • 5

    12、npm install express-list-endpoints

    查看定义的路由

    index.js

    'use strict';
    const express = require('express');
    const listEndpoints = require('express-list-endpoints');
    
    const app = express();
    
    console.log(listEndpoints(app));
    
    app.use(express.static('public'));
    
    app.get('/greet', (req, res) => {
     res.writeHead(200, { 'Content-Type': 'text/html' });
     res.write(`Hello, ${req.query.first_name} ${req.query.last_name}`);
     res.end();
    });
    app.get('/greet/:firstName/:lastName', (req, res) => {
     res.writeHead(200, { 'Content-Type': 'text/html' });
     res.write(`Hello, ${req.params.firstName} ${req.params.lastName}`);
     res.end();
    });
    
    console.log(listEndpoints(app));
    
    app.listen(3000);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    得到结果,可见定义后输出了我们定义的路由

    [ { path: '', methods: [], middlewares: [] } ]
    [
      { path: '/greet', methods: [ 'GET' ], middlewares: [ 'anonymous' ] },
      {
        path: '/greet/:firstName/:lastName',
        methods: [ 'GET' ],
        middlewares: [ 'anonymous' ]
      }
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    13、uuid

    一个debug小技巧,用nodemon启动index.js如果有bug很难看出问题,而换成node启动则会直接报错提示,便于调试

    安装uuid

    npm install uuid
    
    • 1

    index.js文件

    'use strict';
    
    const uuid = require('uuid');
    const express = require('express');
    const app = express();
    
    app.use(express.static('public'));
    
    
    app.get('/greet', (req, res) => {
        req.my_data = {
            start_at: new Date(),
            request_id: uuid.v4()
        };
    
        console.log(`Request complete -- path:${req.path}, status:${res.statusCode}, id:${req.my_data.request_id}`)
    
        res.writeHead(200, { 'Content-Type': 'text/html' });
        res.write(`Hello, ${req.query.first_name} ${req.query.last_name}`);
        res.end();
    });
    
    app.listen(3000);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    也可以把greet中的语句分开写,但需要加入next(),不然不会执行下一个greet,而是直接结束该函数的执行

    'use strict';
    
    const uuid = require('uuid');
    const express = require('express');
    const app = express();
    
    app.use(express.static('public'));
    
    app.get('/greet', (req, res, next) => {
        req.my_data = {
            start_at: new Date(),
            request_id: uuid.v4()
        };
    
        next();
    });
    
    app.get('/greet', (req, res, next) => {
    
        console.log(`Request complete -- path:${req.path}, status:${res.statusCode}, id:${req.my_data.request_id}`)
        next();
    });
    
    app.get('/greet', (req, res, next) => {
    
        res.writeHead(200, { 'Content-Type': 'text/html' });
        res.write(`Hello, ${req.query.first_name} ${req.query.last_name}`);
        res.end();
    
    });
    
    app.listen(3000);
    
    • 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
    • 31
    • 32

    也可以定义到一个get里面,但next还是不能省

    'use strict';
    
    const uuid = require('uuid');
    const express = require('express');
    const app = express();
    
    app.use(express.static('public'));
    
    app.get('/greet',
        (req, res, next) => {
            req.my_data = {
                start_at: new Date(),
                request_id: uuid.v4()
            };
            next();
        },
        (req, res, next) => {
            res.writeHead(200, { 'Content-Type': 'text/html' });
            res.write(`Hello, ${req.query.first_name} ${req.query.last_name}`);
            res.end();
            next();
        },
        (req, res, next) => {
            console.log(`Request complete -- path:${req.path}, status:${res.statusCode}, id:${req.my_data.request_id}`)
            next();
        }
    );
    
    app.listen(3000);
    
    • 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

    还可以单独写成函数,便于阅读

    'use strict';
    
    const uuid = require('uuid');
    const express = require('express');
    const app = express();
    
    app.use(express.static('public'));
    
    function preResponse(req, res, next) {
        req.my_data = {
        start_at: new Date(),
        request_id: uuid.v4()
        };
        next();
    }
    
    function postResponse(req, res, next) {
        console.log(`Request complete -- path:${req.path}, status:${res.statusCode}, id:${req.my_data.request_id}`)
        next(); 
    }
    
    app.get('/greet',
        preResponse,
        (req, res, next) => {
            res.writeHead(200, { 'Content-Type': 'text/html' });
            res.write(`Hello, ${req.query.first_name} ${req.query.last_name}`);
            res.end();
            next();
        },
        postResponse
    );
    
    app.listen(3000);
    
    • 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
    • 31
    • 32
    • 33

    将preResponse和postResponse绑定到所有的请求上(但访问http://127.0.0.1:3000不会触发这两个函数)

    'use strict';
    
    const uuid = require('uuid');
    const express = require('express');
    const app = express();
    
    app.use(express.static('public'));
    
    function preResponse(req, res, next) {
        req.my_data = {
        start_at: new Date(),
        request_id: uuid.v4()
        };
        next();
    }
    
    function postResponse(req, res, next) {
        console.log(`Request complete -- path:${req.path}, status:${res.statusCode}, id:${req.my_data.request_id}`)
        next(); 
    }
    
    app.use(preResponse);
    
    app.get('/greet', (req, res, next) => {
        res.writeHead(200, { 'Content-Type': 'text/html' });
        res.write(`Hello, ${req.query.first_name} ${req.query.last_name}`);
        res.end();
        next();
    });
    
    app.get('/hello', (req, res, next) => {
        res.writeHead(200, { 'Content-Type': 'text/html' });
        res.write(`Hello World`);
        res.end();
        next();
    });
    
    app.use(postResponse);
    
    app.listen(3000);
    
    • 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
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    这样访问http://127.0.0.1:3000/hello或者http://127.0.0.1:3000/greet都会触发preResponse和postResponse

    14、GET FORM

    index.html文件

    <html>
        <body>
            <p>
                Hello, static page!
            p>
            <div id="mascot">
                <a href="https://www.google.com/search?q=silly+cats">
                    <img src="/image/deeplearning.jpg" />
                a>
            div>
            <div id="form-container">
                <form ACTION="/greet" METHOD="GET">
                    <label for="first_name">First name:label><input type="text" name="first_name" id="first_name" />
                    <br />
                    <label for="last_name">Last name:label><input type="text" name="last_name" id="last_name" />
                    <br />
                    <input type="submit">
                form>
            div>
        body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    js文件

    'use strict';
    
    const uuid = require('uuid');
    const express = require('express');
    const app = express();
    
    app.use(express.static('public'));
    
    function preResponse(req, res, next) {
        req.my_data = {
        start_at: new Date(),
        request_id: uuid.v4()
        };
        next();
    }
    
    function postResponse(req, res, next) {
        console.log(`Request complete -- path:${req.path}, status:${res.statusCode}, id:${req.my_data.request_id}`)
        next(); 
    }
    
    app.use(preResponse);
    
    app.get('/greet', (req, res, next) => {
        res.writeHead(200, { 'Content-Type': 'text/html' });
        res.write(`Hello, ${req.query.first_name} ${req.query.last_name}`);
        res.end();
        next();
    });
    
    app.get('/hello', (req, res, next) => {
        res.writeHead(200, { 'Content-Type': 'text/html' });
        res.write(`Hello World`);
        res.end();
        next();
    });
    
    app.use(postResponse);
    
    app.listen(3000);
    
    • 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
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    这样进入http://127.0.0.1:3000/时,会自动进入public文件夹下的index.html,然后会看到表格,输入姓和名,点击submit,会自动请求greet并将填写的内容作为参数传递,server收到后将得到的参数写到一个简单的html源码内并返回,此时浏览器客户端页面自动跳转,将看到自己填写的姓和名

    GET 不够安全,内容全在url上(http://127.0.0.1:3000/greet?first_name=LUJIA&last_name=ZHONG),也很难传送文件

    15、POST FORM

    使用POST方法,表格内容都在HTTP的request body里;因此需要单独再实现一个app.post,而且参数无法从req.query拿到了

    新的index.html

    <html>
        <body>
            <p>
                Hello, static page!
            p>
            <div class="form-container">
                <h4>GETh4>
                <form ACTION="/greet" METHOD="GET">
                    <label for="first_name">First name:label><input type="text" name="first_name" id="first_name" />
                    <br />
                    <label for="last_name">Last name:label><input type="text" name="last_name" id="last_name" />
                    <br />
                    <input type="submit" value="Say Hello">
                form>
            div>
            <div class="form-container">
                <h4>POSTh4>
                <form ACTION="/greet" METHOD="POST">
                    <label for="first_name">First name:label><input type="text" name="first_name" id="first_name" />
                    <br />
                    <label for="last_name">Last name:label><input type="text" name="last_name" id="last_name" />
                    <br />
                    <input type="submit" value="Say Hello">
                form>
            div>
            <div id="mascot">
                <a href="https://www.google.com/search?q=silly+cats">
                    <img src="/image/deeplearning.jpg" />
                a>
            div>
        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
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    可以用如下代码片段查看body(直接加入到之前的代码里即可

    app.post('/greet', (req, res, next) => {
        let body = '';
        req.on('data', chunk => {
            body += chunk;
        });
        req.on('end', () => {
            console.error(body);
            next();
        });
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    可以在命令行界面得到first_name=Lujia&last_name=Zhong的输出

    继续用如下代码进行调试,这里我们用了内置的querystring包

    const qs = require('querystring');
    
    app.post('/greet', (req, res, next) => {
        let body = '';
        req.on('data', chunk => {
            body += chunk;
        });
        req.on('end', () => {
            const query = qs.parse(body);
            console.error(query);
            next();
        });
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    会得到 [Object: null prototype] { first_name: ‘Lujia’, last_name: ‘Zhong’ }

    当然以上的调试代码会导致浏览器出现Cannot POST /greet,毕竟我们并没有写如何回应浏览器的代码

    完整index.js代码,此时使用POST表格正常跳转,且可见参数并不在url上

    'use strict';
    
    const uuid = require('uuid');
    const express = require('express');
    const app = express();
    
    app.use(express.static('public'));
    
    function preResponse(req, res, next) {
        req.my_data = {
        start_at: new Date(),
        request_id: uuid.v4()
        };
        next();
    }
    
    function postResponse(req, res, next) {
        console.log(`Request complete -- path:${req.path}, status:${res.statusCode}, id:${req.my_data.request_id}`)
        next(); 
    }
    
    app.use(preResponse);
    
    app.get('/greet', (req, res, next) => {
        res.writeHead(200, { 'Content-Type': 'text/html' });
        res.write(`Hello, ${req.query.first_name} ${req.query.last_name}`);
        res.end();
        next();
    });
    
    app.get('/hello', (req, res, next) => {
        res.writeHead(200, { 'Content-Type': 'text/html' });
        res.write(`Hello World`);
        res.end();
        next();
    });
    
    const qs = require('querystring');
    
    app.post('/greet', (req, res, next) => {
        let body = '';
        req.on('data', chunk => {
            body += chunk;
        });
        req.on('end', () => {
            const query = qs.parse(body);
            res.writeHead(200, { 'Content-Type': 'text/html' });
            res.write(`Hello, ${query.first_name} ${query.last_name}`);
            res.end();
            next();
        });
    });
    
    app.use(postResponse);
    
    app.listen(3000);
    
    • 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
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    16、body-parser解析body,而不用querystring

    下载body-parser

    npm install body-parser
    
    • 1

    index.js,唯一改的就是引入库和POST那几行

    'use strict';
    
    const uuid = require('uuid');
    const express = require('express');
    const app = express();
    
    app.use(express.static('public'));
    
    function preResponse(req, res, next) {
        req.my_data = {
        start_at: new Date(),
        request_id: uuid.v4()
        };
        next();
    }
    
    function postResponse(req, res, next) {
        console.log(`Request complete -- path:${req.path}, status:${res.statusCode}, id:${req.my_data.request_id}`)
        next(); 
    }
    
    app.use(preResponse);
    
    app.get('/greet', (req, res, next) => {
        res.writeHead(200, { 'Content-Type': 'text/html' });
        res.write(`Hello, ${req.query.first_name} ${req.query.last_name}`);
        res.end();
        next();
    });
    
    app.get('/hello', (req, res, next) => {
        res.writeHead(200, { 'Content-Type': 'text/html' });
        res.write(`Hello World`);
        res.end();
        next();
    });
    
    const BodyParser = require('body-parser');
    const bodyParser = BodyParser.urlencoded({ extended: false });
    
    app.post('/greet', bodyParser, (req, res, next) => {
        res.writeHead(200, { 'Content-Type': 'text/html' });
        res.write(`Hello, ${req.body.first_name} ${req.body.last_name}`);
        res.end();
        next();
    });
    
    app.use(postResponse);
    
    app.listen(3000);
    
    • 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
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    Html文件不变,可见bodyParser的传入使得能够直接从req访问到body以及其中的参数

    17、FORM上传文件

    index.html

    <html>
        <body>
            <p>
                Hello, static page!
            p>
        <div class="form-container">
            <h4>POSTh4>
            <form ACTION="/greet" METHOD="POST" ENCTYPE="multipart/form-data">
                <label for="first_name">First name:label><input type="text" name="first_name" id="first_name" />
                <br />
                <label for="last_name">Last name:label><input type="text" name="last_name" id="last_name" />
                <br />
                <label for="avatar">Avatar:label><input type="file" name="avatar_pic" id="avatar" />
                <br />
                <input type="submit" value="Say Hello">
            form>
        div>
        body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    其中ENCTYPE被指定成了"multipart/form-data",因为一般情况下,如果POST上传的东西有二进制的数据,比如这个图片,而不只是名字等字符串,或者当上传文件特别大时,我们选择使用multipart/form-data;

    其他情况则用application/x-www-form-urlencoded,这个是默认值,不用自己指定

    index.js,调试用

    'use strict';
    
    const uuid = require('uuid');
    const express = require('express');
    const app = express();
    
    app.use(express.static('public'));
    
    function preResponse(req, res, next) {
        req.my_data = {
        start_at: new Date(),
        request_id: uuid.v4()
        };
        next();
    }
    
    function postResponse(req, res, next) {
        console.log(`Request complete -- path:${req.path}, status:${res.statusCode}, id:${req.my_data.request_id}`)
        next(); 
    }
    
    app.use(preResponse);
    
    app.get('/greet', (req, res, next) => {
        res.writeHead(200, { 'Content-Type': 'text/html' });
        res.write(`Hello, ${req.query.first_name} ${req.query.last_name}`);
        res.end();
        next();
    });
    
    app.post('/greet', (req, res, next) => {
        let body = '';
        req.on('data', chunk => {
            body += chunk;
        });
        req.on('end', () => {
            console.log(body);
            res.end('DEVELOPING'); 
            next();
        });
    });
    
    app.use(postResponse);
    
    app.listen(3000);
    
    • 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
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    浏览器正常收到返回的DEVELOPING,命令行查看输出的body

    ------WebKitFormBoundaryRBmt4TEHt22fuEXP
    Content-Disposition: form-data; name="first_name"
    
    Lujia
    ------WebKitFormBoundaryRBmt4TEHt22fuEXP
    Content-Disposition: form-data; name="last_name"
    
    Zhong
    ------WebKitFormBoundaryRBmt4TEHt22fuEXP
    Content-Disposition: form-data; name="avatar_pic"; filename="948-bag-of-tokens.cpp"
    Content-Type: text/plain
    
    ///
    // a very clear solution
    
    // Time Complexity: O(nlogn)
    // Space Complexity: O(logn)
    ///
    class Solution {
    public:
        int bagOfTokensScore(vector& tokens, int power) {
            sort(tokens.begin(), tokens.end());
            int res = 0, scores = 0, i = 0, j = tokens.size() - 1;
            while (i <= j) {
                if (power >= tokens[i]) {
                    power -= tokens[i++];
                    res = max(res, ++scores);
                } else if (scores > 0) {
                    scores--;
                    power += tokens[j--];
                } else {
                    break;
                }
            }
            return res;
        }
    };
    ------WebKitFormBoundaryRBmt4TEHt22fuEXP--
    
    Request complete -- path:/greet, status:200, id:35055e0d-8cc9-4ff2-8af2-9dedc25f4486
    
    • 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
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    可以看到能够接收到文件的内容

    继续调试,使用multer,并用其来保存接收到的文件

    npm install multer
    
    • 1

    js代码

    'use strict';
    
    const uuid = require('uuid');
    const express = require('express');
    const app = express();
    
    app.use(express.static('public'));
    
    function preResponse(req, res, next) {
        req.my_data = {
        start_at: new Date(),
        request_id: uuid.v4()
        };
        next();
    }
    
    function postResponse(req, res, next) {
        console.log(`Request complete -- path:${req.path}, status:${res.statusCode}, id:${req.my_data.request_id}`)
        next(); 
    }
    
    app.use(preResponse);
    
    app.get('/greet', (req, res, next) => {
        res.writeHead(200, { 'Content-Type': 'text/html' });
        res.write(`Hello, ${req.query.first_name} ${req.query.last_name}`);
        res.end();
        next();
    });
    
    const UPLOAD_DIR = `${__dirname}/upload`;
    const Multer = require('multer');
    const upload = Multer({ dest: UPLOAD_DIR });
    
    app.post('/greet', upload.single('avatar_pic'), (req, res, next) => {
        console.log(req.body, req.file);
        res.end('DEVELOPING'); 
    
        next();
    });
    
    app.use(postResponse);
    
    app.listen(3000);
    
    • 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
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    upload文件会被自动创建,并将上传的文件自动保存下来

    输出如下,可以看到multer像querystring一样把参数解析出来了

    [Object: null prototype] { first_name: 'Lujia', last_name: 'Zhong' } {
      fieldname: 'avatar_pic',
      originalname: '948-bag-of-tokens.cpp',
      encoding: '7bit',
      mimetype: 'text/plain',
      destination: '/usr/src/app/upload',
      filename: 'ac4c13e4064867d0e1dab05206943b39',
      path: '/usr/src/app/upload/ac4c13e4064867d0e1dab05206943b39',
      size: 847
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    18、客户端查看server本地文件

    index.js

    'use strict';
    
    const uuid = require('uuid');
    const express = require('express');
    const app = express();
    
    function preResponse(req, res, next) {
        req.my_data = {
        start_at: new Date(),
        request_id: uuid.v4()
        };
        next();
    }
    
    function postResponse(req, res, next) {
        console.log(`Request complete -- path:${req.path}, status:${res.statusCode}, id:${req.my_data.request_id}`)
        next(); 
    }
    
    app.use(preResponse);
    
    app.use(express.static('public'));
    app.use('/assets', express.static('upload'));
    
    app.use(postResponse);
    
    app.listen(3000);
    
    • 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

    此时输入http://127.0.0.1:3000/assets/ac4c13e4064867d0e1dab05206943b39,最后这一串是某个文件的名字,随后该文件会被直接下载下来,而不是显示在浏览器

    问题出在mime-type,浏览器使用mime-type来决定文件的处理方式,就像前面multer解析出来的:mimetype: ‘text/plain’,如果是图片则为image/jpeg

    但因为之前multer保存下来的文件都没有类型后缀,所以我们需要改一改使用multer时的代码

    index.js

    'use strict';
    
    const uuid = require('uuid');
    const express = require('express');
    const app = express();
    
    app.use(express.static('public'));
    app.use('/assets', express.static('upload'));
    
    function preResponse(req, res, next) {
        req.my_data = {
        start_at: new Date(),
        request_id: uuid.v4()
        };
        next();
    }
    
    function postResponse(req, res, next) {
        console.log(`Request complete -- path:${req.path}, status:${res.statusCode}, id:${req.my_data.request_id}`)
        next(); 
    }
    
    app.use(preResponse);
    
    
    const UPLOAD_DIR = `${__dirname}/upload`;
    const Multer = require('multer');
    const multerStorage = Multer.diskStorage({ 
        destination: UPLOAD_DIR,
        filename: function(req, file, cb) { 
            cb(null, uuid.v4() + '_' + file.originalname);
        }
    });
    const upload = Multer({ storage: multerStorage });
    
    app.post('/greet', upload.single('avatar_pic'), (req, res, next) => {
        res.writeHead(200, { 'Content-Type': 'text/html' });
        res.write(`Hello, ${req.body.first_name} ${req.body.last_name}
    ${req.file.filename}
    " />`
    ); res.end(); next(); }); app.use(postResponse); app.listen(3000);
    • 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
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    index.html和之前一样

    <html>
        <body>
            <p>
                Hello, static page!
            p>
        <div class="form-container">
            <h4>POSTh4>
            <form ACTION="/greet" METHOD="POST" ENCTYPE="multipart/form-data">
                <label for="first_name">First name:label><input type="text" name="first_name" id="first_name" />
                <br />
                <label for="last_name">Last name:label><input type="text" name="last_name" id="last_name" />
                <br />
                <label for="avatar">Avatar:label><input type="file" name="avatar_pic" id="avatar" />
                <br />
                <input type="submit" value="Say Hello">
            form>
        div>
        body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    提交后浏览器结果
    在这里插入图片描述
    当然如果提交的是文本,标签是不能显示的,但现在不论提交什么都能在server端找到文件,且文件后缀和提交的保持一致

    可以通过assets直接访问:
    (1)http://127.0.0.1:3000/assets/4b8ec1ee-3a37-4a3c-ba5f-655bd6387d1c_948-bag-of-tokens.cpp
    在这里插入图片描述
    (2)对图片http://127.0.0.1:3000/assets/b22832bf-f3bd-4b84-afcf-94ea00f11d3b_rick.jpg
    在这里插入图片描述

  • 相关阅读:
    光伏电站绝缘阻抗异常排查方法
    Spark安装教程
    杨辉三角形C++
    Android流式布局
    《c++并发编程实战》之第3章 线程共享
    Huffman哈夫曼树思想即代码
    使用vTESTstudio将CANoe项目导入vTESTstudio_02进行编程
    SpringBoot项目使用JSP
    《服务器无状态设计:为什么&如何实现无状态API?》
    【无标题】
  • 原文地址:https://blog.csdn.net/weixin_42815846/article/details/126792126