Node — 第七天 (大事件项目接口实现一)

关于JS错误处理

node中和mysql中的错误处理

node和MySQL提供的方法,已经对错误信息进行了封装,只需要使用 err.message 即可获取到错误信息。

比如:

const fs = require('fs');
// 读取一个不存在的文件
fs.readFile('abcd.txt', (err, data) => {
    if (err) return console.log(err.message); 
    // ENOENT: no such file or directory, open 'abcd.txt'
    console.log(data);
});

比如:试着修改下面的代码,让代码产生错误,然后可以通过 err.message获取到错误信息

function db (sql, params, cb) {
    const mysql = require('mysql');
    const conn = mysql.createConnection({
        host: 'localhost',
        user: 'root',
        password: '12345678',
        database: 'user'
    });
    conn.connect();
    conn.query(sql, params, cb);
    conn.end();
}

db('insert into books set ?', {
    bookname: 'aaa', 
    author: 'bbb', 
    publisher: 'ccc'
}, (err, result) => {
    if (err) return console.log(err.message);
    console.log(result);
});

JS中的错误处理


// 使用try...catch...finally...这种语句可以获取错误信息
// try : 尝试
// catch : 捕获、抓
// finally : 最终

// 语法
/**
 * try { ... } catch (e) { ... }
 * 
 * try { ... } finally { ... }
 * 
 * try { ... } catch (e) { ... } finally { ... }
 */

// function abc () {
//     console.log(123);
// }

// try {
//     abc();
// } catch (e) {
//     // e 是一个错误对象
//     console.log(e.message); // abc is not defined
// }

try {
    abc();
} catch (e) {
    console.log(e.message);
} finally {
    console.log('哈哈哈');
}

try里面有大段的代码,那么也是执行这一大段代码,如果哪一行出了错误,会终止try里面代码的执行,并且会把错误交给catch来处理。

new Error()

Error是JS内置对象,用于创建错误对象。

语法:

new Error('错误信息', '产生错误的文件', '错误的行号');
// 使用的时候,一般后面两个参数不用填,默认使用当前的文件,错误的行号使用产生错误哪一行的行号。

throw关键字

throw 关键字用于抛出错误。

throw 后面可以跟数字、字符串、对象等等。

通过throw抛出的错误,可以被catch捕获到。

  • throw 字符串
    • catch 中,使用 e 获取错误信息
  • throw new Error(‘哈哈哈’);
    • catch 中,使用 e.message 获取错误信息
try {
    // throw 'hahah';
    throw new Error('我自己定的错误');
} catch (e) {
    // console.log(e);
    console.log(e.message);
}
//===============================================================================

搭建大事件接口项目

已知使用到的第三方模块有:

//设置跨域
const cors = require('cors');
//路径查找
const path = require('path');
//设置分类路由,及请求体
const express = require('express');
// 使用express-jwt模块,控制 以 /my 开头的接口,需要正确的token才能访问
const expressJWT = require('express-jwt');
//设置登录权限,加载jsonwebtoken模块(用于生成token加密串)
const jwt = require('jsonwebtoken');
//设置md5的密码加密设置
const utility = require('utility');
//链接数据库
const mysql = require('mysql');
//用于获取formdata类型的请求体,同时完成文件上传
const multer = require('multer');

创建项目目录

创建 big-event-server 文件夹

下载安装第三方模块

npm init -y
npm i express mysql cors multer express-jwt jsonwebtoken
  • express 用于搭建服务器
  • mysql 用于操作数据库
  • cors 用于解决跨域
  • multer 用于完成文件上传
  • express-jwt 用于解密token字符串
  • jsonwebtoken 用于加密token字符串

创建routers文件夹,准备路由文件

项目根目录创建了routers文件夹,里面创建如下四个路由文件

  • user.js 用于完成 个人中心 所需的接口
  • login.js 用于完成 登录、注册接口
  • article.js 用于完成 文章模块 所需的接口
  • category.js 用于完成 文章类别 所需的接口

四个路由文件中,里面添加如下基础代码

const express = require('express');
const router = express.Router();

// router.get('xxx', async (req, res) => {

// });

module.exports = router;

创建app.js,开启服务

const path = require('path');

const express = require('express');
const app = express();
app.listen(3007, () => console.log('大事件服务器启动了'));


// 加载路由模块,并注册成中间件
app.use('/api', require(path.join(__dirname, 'routers', 'login')));
app.use('/my/article', require(path.join(__dirname, 'routers', 'category')));
app.use('/my/article', require(path.join(__dirname, 'routers', 'article')));
app.use('/my', require(path.join(__dirname, 'routers', 'user')));

路由模块的前缀,我们参考 刘龙宾 老师的接口文档,和他的接口一样即可。
大事件接口提示入口:

封装db

/**
 * 导出函数,作用是完成mysql操作(增删改查)
 * @param sql SQL语句
 * @param params 为SQL语句中的占位符传递的值,默认是null
 * @returns Promise对象
 */
module.exports = (sql, params = null) => {
    const mysql = require('mysql');
    const conn = mysql.createConnection({
        host: 'localhost',
        user: 'root',
        password: '12345678',
        database: 'big-event', // 数据库一会创建 
    });
    
    return new Promise((resolve, reject) => {
        conn.connect();
        conn.query(sql, params, (err, result) => {
            err ? reject(err) : resolve(result);
        });
        conn.end();
    });
}

使用Git管理项目

# 初始化
git init

模块化阻止文件 .gitignore

接下来,需要执行add和commit命令把基础的代码提交到本地仓库。但是 第三方模块 没有必要提交到仓库,所以可以设置忽略文件 (.gitignore),内容如下:

# # 表示注释
# 下面设置项目忽略的文件

# 忽略abc.js文件
abc.js

# 忽略xyz文件夹里面所有的文件
xyz

# 忽略 node 第三方模块
package-lock.json
node_modules

设置忽略之后,可以执行 git add .git commit -m '提交了初始的代码'

如果需要上传至 ‘码云’ 或 ‘github’ 教程须知 https://blog.csdn.net/weixin_44694682/category_9920006.html

设置应用级别的中间件

  • 解决跨域

  • post请求体(urlencoded类型,也就是查询字符串类型)

  • 开放静态资源(开放上传后的图片)

/*                   app.js 加入如下代码: */
const cors = require('cors');
// ----------  加载路由模块之前,设置应用级别的中间件
// 解决跨域
app.use(cors());
// 接收 urlencoded 类型的请求体
app.use(express.urlencoded({extended: false}));
// 开放静态资源(uploads)uploads 文件夹要放上传的图片
app.use(express.static('uploads'));

JWT身份认证

  • J: json
  • W:web
  • T:token

就是一种前后端分离模式使用的身份认证方式。

原理图

在这里插入图片描述

实现身份认证

想要完成jwt方式的身份认证,需要一下两个第三方模块

  • express-jwt 用于解密token字符串,还可以控制哪些接口需要身份认证。
  • jsonwebtoken 用于加密token字符串
1、登录的接口,要给客户端返回token
2、控制其他接口,必须携带正确的token才能访问
3、其他需要权限的接口中,能够使用token中保存的数据(用户名)
4、如果身份认证失败,服务器要响应 {status: 1, message: '身份认证失败!'}

登录成功之后,生成token

在login.js中

  • 加载jsonwebtoken模块
  • 使用该模块的 sign 方法生成token加密串
  • 返回给客户端的时候,要在加密串前面加上 “Bearer

参考代码:

/*          login.js  */
// 加载jsonwebtoken模块(用于生成token加密串)
const jwt = require('jsonwebtoken');

// 登录的接口
router.post('/login', async (req, res) => {
    // 假设账号是 admin,密码是 111111
    if (req.body.username === 'admin' && req.body.password === '111111') {
        // 登录成功
        res.json({
            status: 0,
            message: '登录成功',
            // token: 'Bearer ' + jwt.sign(要保存的信息, 秘钥, 配置项)
            // 生成的token前面必须有Bearer,还有一个空格。否则一会token不能正常的解密
            token: 'Bearer ' + jwt.sign({username: 'admin', age: 20}, 'bigevent-9760', {expiresIn: 2*60*1000})
        });
    }
});

使用express-jwt控制 以 /my 开头的接口,必须加入token才能访问

在 app.js 中

  • 加载 express-jwt 模块
  • 配置中间件,指定哪些接口不需要身份认证
// 使用express-jwt模块,控制 以 /my 开头的接口,需要正确的token才能访问
const expressJWT = require('express-jwt');
// 下面一行代码的意思是,除了以 /api 开头的接口,其他所有接口都需要身份认证才能访问
app.use(  expressJWT({ secret: 'bigevent-9760' }).unless({path: /^\/api/})  );

使用Postman来测试

  • 请求的时候,header头如果没有token,是否报错了
  • 请求的时候,header头如果有token,是否可以正常访问

使用错误中间件统一处理身份认证失败的情况

在 app.js 最后的位置,加一个错误中间件

// 错误中间件,统一处理tokne的问题
app.use((err, req, res, next) => {
    // 真的token问题,做判断
    if (err.name === 'UnauthorizedError') {
        console.log(err.message);
        res.json({
            status: 1,
            message: '身份认证失败!'
        });
    }
});

在其他路由中,可以使用req.user对象,获取到token中保存的数据

登录和注册

创建数据库、数据表

创建 big-event数据库

创建user表:

在这里插入图片描述

注册

  • 接收post请求体
  • 写insert语句,完成添加
let r = await db('insert into user set ?', req.body);
if (r.affectedRows > 0) {
    res.json({
        status: 0,
        message: '注册成功'
    });
} else {
    res.json({
        status: 0,
        message: '注册失败'
    });
}

这个代码,不够严谨。如果SQL出现一点点问题,就会报一大段错误,并且也不会做下响应。

使用try…catch 来解决问题

修改后的代码如下:

// 注册的接口
router.post('/reguser', async (req, res) => {
    // 获取post请求体(也就是用户提交的账号和密码)
    // 添加到 user 表 中
    // console.log(req.body); // { username: 'admin', password: '111111' }
    try {
        let r = await db('insert into user set ?', req.body);
        res.json({
            status: 0,
            message: '注册成功'
        });
    } catch (err) {
        console.log(err.message); // 输出这个信息,是为了程序员排错
        res.json({
            status: 1,
            message: '注册失败'
        });
    }
});

思考:

  1. 除了try…catch… 你还能想到其他办法吗?
  2. 其他接口也需要使用try…catch完成SQL的执行,有没有统一的解决办法?

猜你喜欢

转载自blog.csdn.net/weixin_44694682/article/details/105955382