Node学习总结

116 阅读6分钟

1、原生 Node 服务端用法

// 引入对应的包
const http = require('http');
const url = require('url');
const querystring = require('querystring');

// 创建服务
const server = http.createServer((req, res) => {
    // 设置响应的状态码和文本类型    
    res.writeHead(200, {'Content-Type': 'text/html;charset=utf8'});

    // get    
    const reqUrl = req.url;    
    // 将 url 转化成 json
    const query = url.parse(reqUrl, true).query;    
    res.end(`用户名: ${query.username} --- 密码:${query.password}`);    

    // post    
    var postValue = '';
    // 监听请求数据    
    req.on('data', chunk => {        
        postValue += chunk;    
    })
    // 监听请求结束    
    req.on('end', () => {
        // 将数据转化成 json        
        const postData = querystring.parse(postValue);        
        res.end(`用户名: ${postData.username} --- 密码:${postData.password}`);    
    })
})

// 启动服务监听端口
server.listen(9999);

可以用 form 表单进行测试

<form method="post" action="http://localhost:9999">   
    <input type="text" name="username" />    
    <input type="password" name="password" />    
    <input type="submit" value="登录" />
</form>

模拟登录的例子,配合 Mysql 数据库进行使用,可提前在数据库里插入数据

const http = require('http');
const querystring = require('querystring');
const mysql = require('mysql');

const httpServer = http.createServer((req, res) => {    
    if (req.url === '/favicon.ico') return;

    let postValue = '';    
    req.on('data', chunk => {        
        postValue += chunk;    
    })    
    req.on('end', () => {        
        let { username, password } = querystring.parse(postValue);        
       // 创建数据库连接实例        
        const connection = mysql.createConnection({            
            host: 'localhost',            
            port: 3306,            
            user: 'root',            
            password: '123456',            
            database: 'node_demo1'        
        })

        // 建立数据库连接        
        connection.connect();

        // 向数据库发起查询
        connection.query('select * from user where username=? and password=?', [username, password], (error, result, fields) => {            
            if (error) throw error;                        
            if (result.length > 0) {
                // 设置响应的状态码和文本类型以及编码格式                
                res.writeHead(200, {'Content-type': 'text/html;charset=utf-8'});                
                res.write('登录成功!')                
                res.end()            
            }        
        })

        // 断开链接        
        connection.end();    
    })
})

httpServer.listen(9999)

2、Express 框架的 Node 服务端用法

1、基本示例

// 1、导入 express
const express = require("express");

// 2、创建 web 服务器
const app = express();

// 监听客户端的 get 请求
app.get('/user', (req, res) => {    
    // res.send 方法向客户端响应内容    
    // req.query 可以获取到客户端发送过来的查询参数    
    // 默认情况下 req.query 是个空对象    
    res.send(req.query)
})
// 动态参数
app.get('/user/:id', (req, res) => {    
    // req.params 是动态匹配到的 URL 参数,默认也是空对象    
    console.log(req.params.id)    
    res.send(req.params)})
    // 监听客户端的 post 请求
    app.post('/user', (req, res) => {    
    // res.send 方法向客户端响应内容    
    res.send()
})

// 3、启动 web 服务器
app.listen(8000, () => {    
    console.log('express server runing at http://localhost:8000')
})

2、托管静态资源用法

// 调用 express.static() 方法,设置对外的静态资源目录
// 如需托管多个静态资源目录,执行多次以下代码即可
// 如果不同的静态资源目录有同样的静态资源名称,会有拿前面的
app.use(express.static('./test1/public'))  // http://localhost:8000/index.html
// 静态文件目录访问加前缀
app.use('/public', express.static('./test1/public'))  // http://localhost:8000/public/index.html

3、中间件用法(全局和局部)

// 中间件的使用必须在路由前面使用,不然就不生效
// 必须调用 next 函数,不然后续的代码就不会执行
// next 函数后面不要再写业务代码// 连续调用多个中间件,每个中间件都是共享req和res的
// 注册全局中间件
app.use((req, res, next) => {    
    console.log('这是中间件');    
    next();
});
// 定义中间件函数
const mw1 = (req, res, next) => {    
    console.log('局部中间件1');    
    next();
}
const mw2 = (req, res, next) => {    
    console.log('局部中间件2');    
    next();
}
// 在路由中定义的中间件是局部中间件(只在当前路由生效)
app.get('/', mw1, (req, res) => {    
    res.send('home page')
})
// 多个局部中间件
app.get('/', mw1, mw2, (req, res) => {    
    res.send('home page')
})
app.get('/', [mw1, mw2], (req, res) => {    
    res.send('home page')
})

4、对请求体的数据进行解析

// 除了错误级别的中间,其他中间件必须在路由之前注册
// 通过 express.json() 这个中间件,解析表单中的 JSON 格式的数据
app.use(express.json()); 
// 通过 express.urlencoded() 这个中间件,解析表单中的 url-encoded 格式的数据
app.use(express.urlencoded({extends: false})); 
app.post('/user', (req,,res) => {    
    // 在服务器,可以通过 req.body 接收 JSON 格式和 url-encode 的数据    
    // 默认情况下,没有配置解析表单的中间件,req.body 的值为 undefined    
    console.log(req.body);    
    res.send('ok');
})

5、使用路由模块

// 1、导入路由模块const router = require('./router/index.js');// 2、注册路由模块app.use(router);// 添加接口访问前缀app.use('/api', router);

router/index.js

// 1、引入 express
const express = require('express');
// 2、创建路由对象
const router = express.Router();
// 3、挂载路由
router.get('/user/list', (req, res) => {    
    res.send('get user list')
})
router.get('/user/add', (req, res) => {    
    res.send('add new user')
})
console.log('router', router)
// 4、导出路由对象
module.exprot = router;

6、全局错误捕获中间件

// 错误中间件演示
app.get('/', (req, res) => {    
    throw new Error('服务器内部发生了错误');    
    res.send('home page')
})
// 定义错误级别的中间件,捕获整个项目的异常错误,防止程序崩溃
// 错误级别中间件必须注册在所有路由之后,才能捕获到错误
app.use((err, req, res, next) => {    
    console.log('发生了错误' + err.message);    
    res.send('Error', err.message);
})

3、常用的 SQL 语句

查询语句: 

select 列 from 表

增加语句: 

insert into 表 (列, 列, ....) values (值, 值, ...)

where 子句: 

select * from 表 where 列 运算符 值

运算符 and 并且: 

select * from 表 where 条件 and 条件

运算符 or 或者: 

select * from 表 where 条件 or 条件

order by 子句(进行神降序排序)(升序:ASC 降序:DESC,默认升序,可不写):

select * form 表 order by asc/desc

多重排序

例子:对 users 表数据,先按照 status 进行降序,再按 name 进行升序 

select * from users order by status desc, name asc

count(*) 函数 用于返回查询结果的总数据条数:

select count(*) from 表

as 为列设置别名:

select 列 as 别名 from 表

例子:

const mysql = require('mysql2');

const db = mysql.createConnection({
    host: 'localhost',
    port: '7777',
    user: 'root',
    password: '123456',
    database: 'my_basedata'
})

db.connect();
// 查询数据
const sqlStr = 'select * from users';
db.query(sqlStr, (err, res) => {
    if (err) return console.log(err.message); 
    console.log('res', res);
})

// 插入数据
const newUser = { name: 'bb', password: '123' };
const sqlStr2 = 'insert into users (name, password) values (?, ?)';
db.query(sqlStr2, [newUser.name, newUser.password], (err, res) => {
    if (err) return console.log(err.message);
    // 可以通过 affectedRows 属性,来判断插入数据是否成功
    if (res.affectedRows == 1) {
        console.log('数据插入成功')
    }
})
// 便捷方式
const sqlStr2 = 'insert into users set ?';
db.query(sqlStr2, newUser, (err, res) => {
    if (err) return console.log(err.message);
    // 可以通过 affectedRows 属性,来判断插入数据是否成功
    if (res.affectedRows == 1) {
        console.log('数据插入成功')
    }
})

// 更新数据
const updateUser = { name: 'bb_new', password: '123' };
const sqlStr3 = 'update users set name=?, password=? where id=?';
便捷方式
const sqlStr3 = 'update users set ? where id=?';
db.query(sqlStr3, [updateUser, 5], (err, res) => {
    if (err) return console.log(err.message);

    if (res.affectedRows == 1) {
        console.log('数据更新成功')
    }
})

// 删除数据
const sqlStr4 = 'delete from users where id=?';
db.query(sqlStr4, 5, (err, res) => {
    if (err) return console.log(err.message);
    if (res.affectedRows == 1) {
        console.log('删除数据成功')
    }
})

4、session 的使用

主要用于服务端渲染,保存用户登录状态,存放到浏览器 cookie

例子:

const express = require('express');
const app = express();
const session = require('express-session');
// 一定要在路由前面导入
const cors = require('cors');
app.use(cors());

// 解析表单数据中间件
app.use(express.urlencoded({ extended: false }));

app.use(session({
    secret: 'custom', // 自定义
    resave: false,
    saveUninitialized: true
}));

app.post('/api/login', (req, res) => {
    if (req.body.username !== 'admin' && req.body.password !== '123') {
        return res.send({status: 1, msg: '登录失败'});
    }
    // 只有成功配置了 express-session 中间件,才有 req.session
    req.session.user = req.body; // 用户登录信息
    req.session.isLogin = true; // 用户登录状态 
})

// 获取用户姓名的接口
app.get('/api/username', (req,res) => {
    if (!req.session.isLogin) {
        return res.send({status: 1, msg: 'fail'});
    }
    res.send({
        status: 0,
        msg: 'success',
        username: req.session.user.username
    })
})

app.get('/api/logout',  (req,res) => {
    // 清空 session 信息
    req.session.destroy();
    res.send({
        status: 0
        [msg: '退出登录成功'
    })
})

app.listen(9090, () => {
    console.log('enabled server port 9090')
})

5、jwt 的使用

主要用于前后端分离的项目,通过 jwt 来保存登录状态

例子:

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="../jquery.min.js"></script>
</head>
<body>
    <input id="account" type="text" />
    <input id="password" type="text" />
    <button id="login">登录</button>

    <script>
        $('#login').click(function () {
            $.ajax({
                url: 'http://localhost:9090/api/login',
                method: 'post',
                data: {
                    username: $('#account').val(),
                    password: $('#password').val()
                },
                success: function (res) {
                    if (res.status == 200) {
                        window.location.href = '/test3/home.html';
                        localStorage.setItem('token', res.token);
                    }
                }
            })
        })
    </script>
</body>
</html>

home.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="../jquery.min.js"></script>
</head>
<body>
    <h1>home</h1>
    <button id="logout">退出登录</button>

    <script>
        
        $.ajax({
            url: 'http://localhost:9090/admin/userInfo',
            headers: {
                Authorization: 'Bearer ' + localStorage.getItem('token')
            },
            method: 'get',
            success: function (res) {
                console.log('res', res)
            }
        })
        $('#logout').click(function () {
            $.ajax({
                url: 'http://localhost:9090/logout',
                method: 'get',
                success: function () {
                    window.location.href = '/';
                }
            })
        })
    </script>
</body>
</html>

index.js

const express = require('express');
const app = express();
const jwt = require('jsonwebtoken');
const expressJWT = require('express-jwt');

// 一定要在路由前面导入
const cors = require('cors');
app.use(cors());


const secretKey = 'jwt_demo No.1 hhh';

// 将 jwt 字符串解析还原成 JSON 对象的中间件
// 只要配置了 express-jwt 中间件,就可以把解析出来的用户信息,挂在到 req.auth 属性上
app.use(expressJWT.expressjwt({
    secret: secretKey,
    algorithms: ["HS256"],
}).unless({path: [/^\/api\//]}));

// 解析表单数据中间件
app.use(express.urlencoded({ extended: false }));

app.post('/api/login', (req, res) => {
    const userinfo = req.body;
    console.log('userinfo', userinfo.username, userinfo.password)
    if (userinfo.username !== 'admin' || userinfo.password !== '123') {
        console.log('密码错误')
        return res.send({
            status: 400,
            message: '登录失败'
        })
    }

    const tokenStr = jwt.sign({username: userinfo.username}, secretKey, {expiresIn: '30s'});

    res.send({
        status: 200,
        message: '登录成功',
        token: tokenStr
    })
})

app.get('/admin/userInfo', (req, res) => {
    res.send({
        status: 200,
        message: '请求成功',
        data: req.auth
    })
})

app.get('/api/logout', (req, res) => {
    
})

app.use((err, req, res, next) => {
    // 这次错误是由 token 解析失败导致的
    if (err.name === 'UnauthorizedError') {
        return res.send({
            status: 401,
            meesage: '无效的token'
        })
    }
    res.send({
        status: 500,
        message: '未知的错误'
    })
})

app.listen(9090, () => {
    console.log('enabled server port 9090')
})

7、bcrypt 的使用

对密码进行加密

const bcrypt = require('bcryptj');
let password = '';
// bcrypt.hashSync() 对密码进行加密
password = bcrypt.hashSync(password, 10);