Nodejs(二)-搭建服务器

101 阅读6分钟

一、搭建简易服务器

1. 初始搭建

// 0. 引入内置模块
const http = require("http");
const fs = require("fs");
const url = require("url");
const path = require("path");

// 1. 创建一个服务器
const server = http.createServer((req, res) => {
    
    const { query, pathname } = url.parse(req.url, true);
    
    if (/^\/views/.test(pathname)) {
        const { base } = path.parse(pathname);
        
        fs.readFile("./client/views/" + base, "utf-8", (err, data) => {
            if (err) {
                return fs.readFile('./client/views/404.html', 'utf-8', (err, data) => res.end(data))
            }

            res.end(data);
        });
    }

});

// 2. 给服务器添加一个端口号
server.listen(8080, () => console.log("服务器启动成功"));

2. 配置 css

// 0. 引入内置模块
const http = require("http");
const fs = require("fs");
const url = require("url");
const path = require("path");

// 1. 创建一个服务器
const server = http.createServer((req, res) => {
    const { query, pathname } = url.parse(req.url, true);
    // console.log(pathname);

    // 处理 html 结构
    if (/^\/views/.test(pathname)) {
        const { base } = path.parse(pathname);

        fs.readFile("./client/views/" + base, "utf-8", (err, data) => {
            if (err) {
                return fs.readFile(
                    "./client/views/404.html",
                    "utf-8",
                    (err, data) => res.end(data)
                );
            }

            res.end(data);
        });
    }

    /**
     *  处理 css 样式
     *
     *      要求请求css的时候, 以 /style 开头
     *          后续跟上要请求的文件
     */
    if (/^\/style/.test(pathname)) {
        const { base } = path.parse(pathname);

        fs.readFile('./client/css/' + base, 'utf-8', (err, data) => {
            if (err) return console.log(err)

            res.end(data)
        })
    }
});

// 2. 给服务器添加一个端口号
server.listen(8080, () => console.log("服务器启动成功"));

3. 接口和静态资源

// 0. 引入内置模块
const http = require("http");
const fs = require("fs");
const url = require("url");
const path = require("path");

// 1. 创建一个服务器
const server = http.createServer((req, res) => {
    const { query, pathname } = url.parse(req.url, true);

    /**
     *  问题:
     *      目前的方式中, 我们处理了 html css
     *      但是还有很多 静态资源没有处理 (js, img, 音视频.....)
     *
     *
     *      按照我们目前的方式处理, 最终代码一定会很多, 并且 其中有很多代码是重复代码
     *
     *
     *  所以我们之前的解决方式, 仍不足以满足工作中的需求
     *  那么我们做一个新的约定
     *
     *          如果前端需要访问 index.html
     *              请求的路径      /static/index.html
     *          如果前端需要访问 login.js
     *              请求的路径      /static/login.js
     *          如果前端需要访问 list.css
     *              请求的路径      /static/list.css
     *
     *  其实就是请求的时候 统一以 /static 开头, 后续跟上我们需要请求的文件名
     */

    if (/^\/static/.test(pathname)) {
        const { base, ext } = path.parse(pathname);

        let urlStr = "./client/";

        if (ext === ".html") {
            urlStr += "views/";
        } else if (ext === ".css") {
            urlStr += "css/";
        } else if (ext === ".js") {
            urlStr += "js/";
        }

        fs.readFile(urlStr + base, "utf-8", (err, data) => {
            if (err) {
                if (ext === ".html") {
                    return fs.readFile(
                        "./client/views/404.html",
                        "utf-8",
                        (err, data) => {
                            console.log(data);
                            res.end(data);
                        }
                    );
                } else {
                    return res.end("");
                }
            }

            res.end(data);
        });
    }

    // 处理 html 结构
    // if (/^\/views/.test(pathname)) {
    //     const { base } = path.parse(pathname);

    //     fs.readFile("./client/views/" + base, "utf-8", (err, data) => {
    //         if (err) {
    //             return fs.readFile(
    //                 "./client/views/404.html",
    //                 "utf-8",
    //                 (err, data) => res.end(data)
    //             );
    //         }

    //         res.end(data);
    //     });
    // }

    // 处理 css 样式
    // if (/^\/style/.test(pathname)) {
    //     const { base } = path.parse(pathname);

    //     fs.readFile("./client/css/" + base, "utf-8", (err, data) => {
    //         if (err) return console.log(err);

    //         res.end(data);
    //     });
    // }
});

// 2. 给服务器添加一个端口号
server.listen(8080, () => console.log("服务器启动成功"));

4. 添加接口处理

// 0. 引入内置模块
const http = require("http");
const fs = require("fs");
const url = require("url");
const path = require("path");

// 1. 创建一个服务器
const server = http.createServer((req, res) => {
    const { query, pathname } = url.parse(req.url, true);

    // 1.1 静态资源配置 (html, css, js, img, video, mp4...)
    if (/^\/static/.test(pathname)) {
        const { base, ext } = path.parse(pathname);

        let urlStr = "./client/";

        if (ext === ".html") {
            urlStr += "views/";
        } else if (ext === ".css") {
            urlStr += "css/";
        } else if (ext === ".js") {
            urlStr += "js/";
        }

        fs.readFile(urlStr + base, "utf-8", (err, data) => {
            if (err) {
                if (ext === ".html") {
                    return fs.readFile(
                        "./client/views/404.html",
                        "utf-8",
                        (err, data) => {
                            console.log(data);
                            res.end(data);
                        }
                    );
                } else {
                    return res.end("");
                }
            }

            res.end(data);
        });
    }

    // 1.2 处理接口数据 (用户详情, 首页轮播图, 商品列表...)
    if (/^\/api/.test(pathname)) {

        // 如果当前分支执行, 说明前端发了一个请求, 想要获取某个数据, 此时我们应该严格对比请求的地址和方式, 然后对前端的请求做出响应
        // console.log(pathname)

        // 商品列表数据
        if (pathname === '/api/goods/list' && req.method === 'GET') {
            // 1. 获取前端携带的参数
            // 2. 根据前端携带的参数, 去数据库中 寻找对应的数据
            // 3. 将处理完毕的数据, 返回给前端
            const list = [
                {
                    id: 1,
                    name: '商品1'
                },
                {
                    id: 2,
                    name: '商品2'
                },
                {
                    id: 3,
                    name: '商品3'
                }
            ]

            // res.end(list)    // 直接返回数组 会报错
            // res.end('返回了一个字符串, 看看会不会报错')  // 返回字符串没有问题

            // 所以我们将 非字符串的数据, 转换成 JSON 格式的数据 返回, 就不会有报错了
            // res.end(JSON.stringify(list))


            // 开发中后端返回数据一般是对象格式, 对象中 code 就是证明本次请求是否成功
            res.end(JSON.stringify({
                code: 1,
                msg: '商品列表请求成功',
                // list: list
                list
            }))

        }

        // 商品详情
        if (pathname === '/api/goods/info' && req.method === 'POST') {
            // 1. 获取前端携带的参数
            // 2. 根据前端携带的参数, 去数据库中 寻找对应的数据
            // 3. 将处理完毕的数据, 返回给前端
            

            res.end(JSON.stringify({
                code: 1,
                msg: '商品详情请求成功',
                info: {
                    id: 'qf001',
                    name: '商品的名字',
                    title: '商品的标题',
                    price: '$100.00'
                }
            }))

        }


        // 目前我们已经成功的处理了 前端发送的 ajax 请求, 但是我们没有处理对应的传参;  思考: get 的参数, 如何处理?
    }
});

// 2. 给服务器添加一个端口号
server.listen(8080, () => console.log("服务器启动成功"));

5. 处理接口参数

    // 0. 引入内置模块
const http = require("http");
const fs = require("fs");
const url = require("url");
const path = require("path");

// 1. 创建一个服务器
const server = http.createServer((req, res) => {
    const { query, pathname } = url.parse(req.url, true);

    // 1.1 静态资源配置 (html, css, js, img, video, mp4...)
    if (/^\/static/.test(pathname)) {
        const { base, ext } = path.parse(pathname);
        // console.log(path.parse(pathname))
        let urlStr = "./client/";

        if (ext === ".html") {
            urlStr += "views/";
        } else if (ext === ".css") {
            urlStr += "css/";
        } else if (ext === ".js") {
            urlStr += "js/";
        }

        fs.readFile(urlStr + base, "utf-8", (err, data) => {
            if (err) {
                if (ext === ".html") {
                    return fs.readFile(
                        "./client/views/404.html",
                        "utf-8",
                        (err, data) => {
                            res.end(data);
                        }
                    );
                } else {
                    return res.end("");
                }
            }

            res.end(data);
        });
    }

    // 1.2 处理接口数据 (用户详情, 首页轮播图, 商品列表...)
    if (/^\/api/.test(pathname)) {
        // 商品列表数据
        if (pathname === "/api/goods/list" && req.method === "GET") {
            // 1. 获取前端携带的参数
            // 2. 根据前端携带的参数, 去数据库中 寻找对应的数据
            // 3. 将处理完毕的数据, 返回给前端

            res.end(
                JSON.stringify({
                    code: 1,
                    msg: "商品列表请求成功",
                    list: [
                        {
                            id: 1,
                            name: "商品1",
                        },
                        {
                            id: 2,
                            name: "商品2",
                        },
                        {
                            id: 3,
                            name: "商品3",
                        },
                    ],
                })
            );
        }

        // 商品详情
        if (pathname === "/api/goods/info" && req.method === "POST") {
            // 1. 获取前端携带的参数
            // 2. 根据前端携带的参数, 去数据库中 寻找对应的数据
            // 3. 将处理完毕的数据, 返回给前端

            res.end(
                JSON.stringify({
                    code: 1,
                    msg: "商品详情请求成功",
                    info: {
                        id: "qf001",
                        name: "商品的名字",
                        title: "商品的标题",
                        price: "$100.00",
                    },
                })
            );
        }

        // 登录
        if (pathname === "/api/user/login" && req.method === "POST") {
            /**
             *  1. 接收用户传递的参数
             *
             *  在我们的请求报文中有一个方法
             *
             *      req.on()
             *
             *      语法1:
             *          req.on('data', (chunk) => { console.log(chunk 就是请求头内传递的参数, 但是可能会分多次传递) })
             *
             *          我们请求体内的参数会分为多次传递到上边的回调函数内的形参 chunk 中
             *          至于什么时候接收完毕, 我们不知道, 因为上述的代码可能会执行多次
             *
             *          所以这个方法还有一个 语法2, 会在 请求体内的参数接收完毕的时候, 执行
             *      语法2:
             *          req.on('end' () => { console.log(当前回调函数会在请求体内的参数接收完毕的时候执行) })
             */

            let dataStr = "";
            req.on("data", (chunk) => {
                dataStr += chunk;
            });

            req.on("end", () => {
                // console.log("我需要等到参数接收完毕的时候 在执行", dataStr);
                // 当前回调函数执行的时候, dataStr 其实就是我们的 参数

                /**
                 *  一个新的问题:
                 *      此时的参数 dataStr 是 查询字符串 还是 JSON 格式的字符串?
                 *
                 *  现在有一个 世界约定
                 *      我们在开发的时候 前端如果是 POST 方式, 并且需要携带参数
                 *          那么需要在 请求头内 设置 content-type 属性
                 *          目的就是为了告诉后端, 当前的参数格式是什么
                 */

                if (req.headers["content-type"] === "application/json") {
                    dataStr = JSON.parse(dataStr);
                }

                if (
                    req.headers["content-type"] ===
                    "application/x-www-form-urlencoded"
                ) {
                    dataStr = url.parse("?" + dataStr, true).query;
                }

                // 2. 安全校验 (非空, 是否符合正则规则)
                if (dataStr.username === "" || dataStr.password === "") {
                    // 账号或密码 传递的是空字符
                }
                if (
                    dataStr.username === undefined ||
                    dataStr.password === undefined
                ) {
                    // 账号或密码 没有传递
                }

                const nameReg = /^[a-z0-9]\w{4,11}$/;
                const pwdReg = /^\w{6,12}$/;

                if (
                    !nameReg.test(dataStr.username) ||
                    !pwdReg.test(dataStr.password)
                ) {
                    // 账号或密码 格式不对
                }

                // 3. 根据用户传递的参数, 去数据库中查询是否有对应的用户

                // 4. 根据查询的结果, 反馈不同的信息
                res.end(
                    JSON.stringify({
                        code: 1,
                        msg: "登录成功",
                        token: "后端需要返回一个 token, 将来前端会利用 token 证明登陆过, 并且在有效期内",
                    })
                );
            });
        }

        // 购物车列表
        if (pathname === "/api/cart/list" && req.method === "GET") {
            /**
             *  因为 get 方式 只允许传递 查询字符串, 所以在当前服务器一开始的时候就已经将当前请求的 参数 解析完毕
             *
             *  其实当前服务器最开始的第一行代码: const { query, pathname } = url.parse(req.url, true);
             */
            console.log(query);

            res.end(JSON.stringify({
                code: 1,
                msg: '购物车列表获取成功',
                list: '这是一个数组'
            }))
        }
    }
});



// 2. 给服务器添加一个端口号
server.listen(8080, () => console.log("服务器启动成功"));