关于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,密码是 111111if (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: '注册失败'});}
});
思考:
- 除了try…catch… 你还能想到其他办法吗?
- 其他接口也需要使用try…catch完成SQL的执行,有没有统一的解决办法?