關于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的執行,有沒有統一的解決辦法?