nodejs1--后台搭建

Nodejs

主要配置:

1、环境配置:

npm init -y

2、配置淘宝镜像:

npm install -g cnpm --registry=https://registry.npm.taobao.org

3、配置运行命令:

在这里插入图片描述

运行程序:

npm run dev

4、监听代码:

cnpm install nodemon --save-dev

5、运行跨平台设置和使用环境变量的脚本:

cnpm install cross-env --save-dev

在package.json配置nodemon和cross-env

在这里插入图片描述

6、nodejs连接数据库

npm i mongodb --save-dev

7、全部的配置下载

在这里插入图片描述

8、配置
在这里插入图片描述

创建步骤:

1、创建服务

// 创建服务
// 引入http
const http = require('http')
const {serverHandler} = require('../src/handler/serverHandler')

// 新建服务
const server = http.createServer(
    // 处理请求
    serverHandler
);

// 监听服务
server.listen(8090,() => {
    console.log('--服务已启动--');
})

2、处理用户的请求并解析参数,给前台做出响应

// 处理用户的请求,并给前台做出响应
// 参数处理 --- POST和GET
const { parse } = require('url')
const { usersRouter } = require('../router/usersRouter')
const { booksRouter } = require('../router/booksRouter')
const querystring = require('querystring')
const { decode } = require('punycode')

const SESSON_DATA = {}; // 保存用户信息

// 添加cookie中expires中
// 给当前时间添加1分钟
const getExpiresTime = () => {
    const date = new Date();
    date.setMinutes(date.getMinutes() + 1);
    // date.toUTCString 将时间对象转为字符串
    return date.toUTCString();
}
// 解析POST参数方法
const getPOST = (req) => {
    // 解决异步问题 使用Promise构造函数
    return new Promise((resolve, reject) => {
        // 获取POST请求的参数,需要数据的监听--流的方式接收
        let postParams = '';
        req.on("data", chunk => {
            postParams += chunk;
        });
        req.on("end", () => {
            // JSON.parse(postParams) 不解析空 需要判断
            if (postParams && postParams !== '') {
                resolve(JSON.parse(postParams));
            } else {
                resolve({});
            }
        });
    });
}

const serverHandler = (req, res) => {
    // 解决跨域问题
    res.setHeader('Access-Control-Allow-Origin', '*');
    // 设置响应头
    res.setHeader('content-type', 'application/json;charset=utf-8')

    // cookie存储在浏览器端,用户登录一次,我们可以将用户信息存入cookie,下次用户访问时,不需要登录操作,就可以直接使用
    // cookie
    // 1、获取请求中的cookie 获取到的是String
    const cookieStr = req.headers.cookie;
    // 2、解析cookie--querystring
    // 判断cookie是否存在
    let cookies = {};
    if (cookieStr) {
        cookies = querystring.decode(cookieStr, ';', '=');
    }
    // 3、将cookies保存在req中
    req.cookies = cookies;

    // 因为cookie在浏览器端是可以查看的,所以为了增加我们信息的安全性,增加session 在cookie中设置一个令牌,将所有内容放入session中,
    //前端发送请求,判断cookie是否含有令牌,含有令牌不需要登录操作,根据令牌内容,从服务端传入用户信息
    //没有令牌,需要进行登录,并创建令牌
    
    // 4、判断用户是否登录
    // 增加 session 增加安全性---> uuid
    // 解析时:中文字符转换 decodeURIComponent
    // 判断cookie中是否含有uuid
    let uuid = cookies['uuid'] || cookies[' uuid'];
    // 设置一个标识,是否设置cookie
    let isWriteCookie = false;
    if (uuid) {
        // uuid存在,判断SESSON_DATA[uuid]是否有内容
        if (!SESSON_DATA[uuid]) {
            // 没有内容,返回空对象
            SESSON_DATA[uuid] = {}
        }
    } else {
        // uuid不存在,设置cookie,增加uuid
        isWriteCookie = true;
    }

    req.session = SESSON_DATA[uuid];

    // 解析rul--使用parse,需要引用
    // GET和POST解析方法不同
    // GET参数
    // <1>获取请求的url
    const { url } = req;
    // <2>解析url
    const obj = parse(url, true); // 解析url
    const { query, pathname } = obj;
    // <3>将get请求的参数放置在req.query
    req.pathname = pathname; // 将get请求的参数放置在req.query中
    req.query = query
    
    // POST参数 -- 封装
    getPOST(req).then(data => {
        console.log(isWriteCookie)
        if (isWriteCookie) {
            // res.end(JSON.stringify({
            //     message:'请登录'
            // }))
            req.body = data;
            // req.session = SESSON_DATA[uuid];
            usersRouter(req)?.then(data => {
                // if(data === undefined){
                //     return;
                // }
                // cookie
                if (data.data && data.data.length > 0) {
                    // 设置时:中文字符的转换  encodeURIComponent
                    uuid = `${new Date().getTime()}_${Math.random(10000000000) + 1}`;
                    res.setHeader("Set-Cookie", `uuid=${uuid}`);
                } 
                SESSON_DATA[uuid] = data.data;
                res.end(JSON.stringify(data))
                return;
            });
            booksRouter(req)?.then(data => {
                res.end(JSON.stringify(data))
                return;
            });
        } else {
            console.log("bbbb")
            req.body = data;
            usersRouter(req)?.then(data => {
                console.log(data)
                res.end(JSON.stringify(data))
                return;
            });
            booksRouter(req)?.then(data => {
                res.end(JSON.stringify(data))
                return;
            });
        }
    })
}

module.exports = {
    serverHandler
}

3、dbutil(数据库连接工具)

发送请求,数据从数据库中获取,需要连接数据库

// 创建客户端
const mongodbClient = require('mongodb').MongoClient;
// 引入url dbname
const { MONGODBU_URL, DB_NAME } = require('../config/dconfig');

// 获取集合
// async 返回Promise对象
const getCollection = async collName => {
    // 匹配环境信息
    // dev环境
    let url = MONGODBU_URL;
    let dataName = DB_NAME;
    const env = process.env.NODE_ENV;
    if (env === "production") {
        url = MONGODBU_URL_PRO;
        dataName = DB_NAME_PRO;
    }
    // 连接数据库
    const conn = await mongodbClient.connect(url)
    // 使用数据库
    const db = await conn.db(dataName);
    // 获取集合
    const  collection = await db.collection(collName);
    return collection;
}
// 查询数据
const searchData = async (collName, params = {}) => {
    const collection = await getCollection(collName)
    // 进行查询
    const data =  await collection.find(params).toArray()
    return data;
}
// 增加数据
const addData = async (collName, params = {}) => {
    // 判断增加内容是否为空
    if (Object.keys(params).length === 0) {
        return Promise.reject({
            ok: false,
            count: 0,
            messagge: '失败,没有参数'
        });
    } else {
        const collection = await getCollection(collName)
        const result = await collection.insertOne(params)
        if (result.acknowledged) {
            return Promise.resolve({
                ok: true,
                count: 1,
                message: '成功'
            });
        } else {
            return Promise.resolve({
                ok: false,
                count: 0,
                message: '失败'
            });
        }
    }
}

// 修改数据
const upDate = async (collName,filterParam, params = {}) => {
   
        const collection = await getCollection(collName)
        // 进行查询
        const result = await collection.updateOne(filterParam,{$set:params})
        return result;
}

// 删除数据
const remDate = async (collName,params = {}) => {
    const result = await getCollection(collName).deleteOne(params)
    return result;
}

module.exports = {
    searchData,
    addData,
    upDate,
    remDate
}

4、router(处理请求的方法放入路由中)

请求分为对书的处理和对用户的处理,所以分为两个路由

从数据库中得到数据后,响应给前端

<1>booksRouter

const { parse } = require('url');
const { getBook, addBook, upBook ,remBook} = require('../data/books')
// 拿到请求需要判断请求的方式和pathname
const booksRouter = req => {
    const { method, pathname } = req;
    // req.query = obj.query;
    // 获取pathName
    // const pathName = pathname;
    // 判断请求方式
    if (method === "GET" && pathname === "/api/getBookList") {
        // 获取数据库数据
        return getBook(req.query);
    } else if (method === 'POST' && pathname === '/api/addBookList') {
        return addBook(req.body);
    } else if (method === 'POST' && pathname === '/api/updateBookList') {
        return upBook(req.body);
    } else if (method === 'POST' && pathname === '/api/remdateBookList') {
        return remBook(req.body);
    } 
    // else if (method === 'GET'&& pathname === '/api/logs') {
    //     // 读取日志内容

    // }
}

module.exports = {
    booksRouter
}

<2>usersRouter

// 判断method
const { parse } = require('url');
const { getUser,upUser } = require('../data/users');

const usersRouter = req => {
    const { method, pathname } = req;
    // const { method, url } = req;
    // const obj = parse(url, true);
    // 获取pathName
    // const pathName = obj.pathname;
    // 判断请求方式
    if (method === "POST" && pathname === "/api/login") {
        // 获取数据库数据
        const result = getUser(req.body);
        return Promise.resolve(result);
    } else if (method === "POST" && pathname === '/api/modifyPas') {
        const result = upUser(req.body);
        return Promise.resolve(result);
    } 
}
module.exports = {
    usersRouter
}

5、为了增加安全性,我们也需要对密码进行加密操作

// 加密
// 引入
const crypto = require('crypto')

// 对密码进行加密
const secretPassword = password => {
    // 创建并返回一个hash对象,它是一个指定算法的加密hash
    /*
        Hmac算法也是一种哈希算法,它可以利用MD5或SHA1等哈希算法
        Hmac还需要一个密钥
    */
    const hash = crypto.createHash('md5');
    return hash.update(password).digest('hex')
}
module.exports = {
    secretPassword
}

6、我们在生产模式下,是没有控制台的,所以需要用日志文件,将所用的访问信息,报错信息等都可以写入日志文件中

一、文件的基本操作

在nodejs中所有与文件相关的操作都在fs模块中,而读写操作又是我们会经常用到的操作。

nodejs的fs模块针对读操作为我们提供了readFile,read, createReadStream三个方法,针对写操作为我们提供了writeFile,write, createWriteStream三个方法。

<1>引入模块 fs, path

const fs = require('fs');
const path = require('path');

<2>readFile和readFileSync

readFile方法是通过异步的方式将要读取的文件内容完整读入缓存区,再从该缓存区中读取文件内容,且没有返回值,具体语法:readFile(path[, options], callback)。

path:表示文件路径,经常使用 path来拼接路径,path.resolve(__dirname,‘目录1’,‘目录2’,‘文件名’)

__dirname:表示当前路径

        // 获取文件路径,__dirname表示当前路径
        const filePath = path.resolve(__dirname,'../','logs','root.log');

options:是一个对象,包含两个属性encoding表示字符编码和flag表示文件系统标志。当没有指定字符编码时,则返回原始的 buffer,当为字符串时只能表示字符编码

        fs.readFile(filePath,"utf-8",(err,data)=>{
            if(!err){
                console.log(data.toString());
            }     
        });

callback:表示一个回调函数,会传入两个参数 (err, data),其中 data 是文件的内容,err是错误信息,没有则为null。

        fs.readFile(filePath,(err,data)=>{
            if(!err){
                console.log(data.toString());
            }     
        });

<3>readFileSync和readFile基本一致,唯一的区别是readFileSync使用同步的方式读取数据,且读取的内容通过返回值获取。具体语法:fs.readFileSync(path[, options])。在使用过程中,如果指定了 encoding 选项,则此函数返回字符串。 否则,返回 buffer。

       const data = fs.readFileSync(filePath,'utf-8');
       console.log(data.toString());

<4>通过Promise实现readFile

在js中通过require(‘fs’).promises或require(‘fs/promises’)调用。

const fsPromise = require('fs').promises;

具体语法:fsPromises.readFile(path[, options]);

       fsPromise.readFile(filePath,'utf-8').then(data=>{
        console.log('promise:',data);
       });

将异步变成同步

// 封装打印函数
const print = async ()=>{
    const filePath = path.resolve(__dirname,'../','logs','root.log');
    const data = await fsPromise.readFile(filePath);
    return data.toString();
}

//调用
console.log('promise:', await print());

<5> 通过Filehandle实现

FileHandle对象是数字文件描述符的包装器,由fsPromises.open()方法在内部创建。具体语法是:filehandle.readFile(encoding);执行语句返回值就是我们读取的内容。

        // 使用fileHandle来实现
        // 创建fileHandle
        const fileHandle = await fsPromise.open(filePath); // Promise<FileHandle>
        // 通过readFile去读取内容
        const data = (await fileHandle.readFile()).toString();

<6>写的方法,与readFile很相似

// req.headers['user-agent'] 获取浏览器信息         
const filePath = path.resolve(__dirname,'../logs','access.log');
         const content = `${moment().format()}-${req.method}-${req.headers['user-agent']}:URL-${pathname}\n`;
         await fsPromise.writeFile(filePath,content,{flag:'a'})

<7> 文件IO的性能瓶颈:慢。解决办法,nodejs/stream

  1. 标准的输入输出

    process.stdin 获取数据 process.stdout 输出数据

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KB8uPnch-1675514380250)(C:\Users\27306\AppData\Roaming\Typora\typora-user-images\image-20230202165129699.png)]

    process.stdin.pipe(process.stdout)

  2. 备份文件

            // source
            const source = path.resolve(__dirname, '../logs', 'access.log');
            //  read stream
            const rs = fs.createReadStream(source);
            // dest
            const dest = path.resolve(__dirname, '../logs/bak', `access${moment().format('YYYYMMDDHHmmss')}.log`);
            // write stream
            const ws = fs.createWriteStream(dest, { flags: 'a', encoding: 'utf-8' });
            rs.pipe(ws);
    
            rs.on('end', () => {
                console.log("复制完成");
            })
    
  3. 使用writeStream去写

    const createWS = filename=>{
        const filePath = path.resolve(__dirname, '../logs', filename);
        return fs.createWriteStream(filePath,{flags:'a',encoding:'utf-8'});
    }
    
     const content = `${moment().format()}-${req.method}-${req.headers['user-agent']}:URL-${pathname}\n`;
        // 使用流的方式去写
        createWS('access.log').write(content);
    
  4. 逐行读取文件 readline

    <1> 先进行安装

    npm install readline --save

    <2>引用

    const readline = require('readline');
    

    <3>使用

       const source = path.resolve(__dirname, '../logs', 'access.log');
            const rs = fs.createReadStream(source);
            const rlInterface = readline.createInterface(rs);
    
            let sum = 0;
            let edgCount = 0;
            rlInterface.on('line', data => {
                if (data.trim().length === 0) {
                    return;
                }
                sum += 1;
                if (data.indexOf('Edg') > -1) {
                    edgCount += 1;
                }
            });
    
            rlInterface.on('close', () => {
                console.log('文件读取完成', edgCount / sum);
            });
    
            return { code: 0, message: `统计完成` }
    

    const rlInterface = readline.createInterface(rs);

        let sum = 0;
        let edgCount = 0;
        rlInterface.on('line', data => {
            if (data.trim().length === 0) {
                return;
            }
            sum += 1;
            if (data.indexOf('Edg') > -1) {
                edgCount += 1;
            }
        });
    
        rlInterface.on('close', () => {
            console.log('文件读取完成', edgCount / sum);
        });
    
        return { code: 0, message: `统计完成` }
    

    ``
    注:路由获取数据库中的数据的方法,封装在获取数据的包中(获取书籍数据)

const { searchData,addData,upDate,remDate} = require('../dbutil/mongodbUtil')
// 查找
const getBook = params => {
    const newParams = { ...params };
    if (params.bookname && params.bookname !== '') {
        newParams.bookname = { $regex: `.*${params.bookname}.*` }
    }
    return searchData("books",newParams);
}
// 增加
const addBook = params => {
    return addData("books",params);
}
// 修改
const upBook = (params) => {
    const {bookid,...newObj} = params
    return upDate('books',{bookid},params);
}
// 删除
const remBook = params => {
    // const par= JSON.stringify(params)
    console.log(888888)
    return remDate('books',params);
}
module.exports = {
    getBook,
    addBook,
    upBook,
    remBook
}
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值