Node.js后端开发踩坑记录
搞Node.js后端开发有一段时间了,从环境配置到加密、HTTP请求,记录一下踩过的坑和实战经验。
Node.js环境配置
CentOS安装指定版本
安装Node.js 14.x:
1 2 3
| yum -y install curl curl -sL https://rpm.nodesource.com/setup_14.x | sudo bash - yum install -y nodejs
|
安装最新LTS:
1 2 3 4 5
| curl -sL https://rpm.nodesource.com/setup_lts.x | sudo bash - yum install -y nodejs
node --version npm -v
|
Windows多版本管理
用nvm-windows管理多个版本:
- 下载:https://github.com/coreybutler/nvm-windows/releases
- 安装后验证:
nvm version
配置淘宝镜像(setting.txt):
1 2 3 4
| arch: 64 proxy: none node_mirror: http://npm.taobao.org/mirrors/node/ npm_mirror: https://npm.taobao.org/mirrors/npm/
|
常用命令:
1 2 3 4
| nvm ls nvm install 14.21.3 nvm use 16.13.1 node -v
|
npm镜像源
查看当前源:
国内镜像:
1 2 3 4 5 6 7 8
| npm config set registry https://registry.npmmirror.com
npm config set registry https://mirrors.huaweicloud.com/repository/npm/
npm config set registry http://mirrors.cloud.tencent.com/npm/
|
还原官方源:
1
| npm config set registry https://registry.npmjs.org
|
Yarn换源
1 2
| yarn config get registry yarn config set registry https://registry.npmmirror.com
|
npm安装问题
错误:reify:fsevents sill reify mark deleted
解决:
1 2 3 4
| npm config get registry npm config set registry https://registry.npmjs.org/ npm cache clean --force npm install
|
加密技术
HMAC SHA256
Node.js内置crypto模块,直接用就行。
基础用法:
1 2 3 4 5 6 7 8 9 10
| const crypto = require('crypto');
const secret = 'your-secret-key'; const message = 'data-to-sign';
const hmac = crypto.createHmac('sha256', secret) .update(message) .digest('hex');
console.log('HMAC:', hmac);
|
Base64编码:
1 2 3
| const hmacBase64 = crypto.createHmac('sha256', secret) .update(message) .digest('base64');
|
封装工具类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| const crypto = require('crypto');
class CryptoUtil { static hmacSha256(data, key, encoding = 'base64') { return crypto .createHmac('sha256', key) .update(data) .digest(encoding); }
static hmacSha256Hex(data, key) { return this.hmacSha256(data, key, 'hex'); }
static verifyHmac(data, key, signature, encoding = 'base64') { const computed = this.hmacSha256(data, key, encoding); return computed === signature; } }
module.exports = CryptoUtil;
|
API请求签名示例:
1 2 3 4 5 6 7 8 9 10 11 12
| function signRequest(params, secretKey) { const sortedKeys = Object.keys(params).sort();
const paramString = sortedKeys .map(key => `${key}=${params[key]}`) .join('&');
return CryptoUtil.hmacSha256Hex(paramString, secretKey); }
|
MD5哈希
虽然MD5不推荐用于安全场景,但数据校验还是可以用。
1
| npm install crypto --save
|
基础用法:
1 2 3 4 5 6 7 8 9 10 11 12
| const crypto = require('crypto');
function md5(str) { return crypto.createHash('md5') .update(String(str)) .digest('hex'); }
const password = 'userPassword123'; const salt = 'randomSalt'; const hashedPassword = md5(`${password}${salt}`);
|
封装工具:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| const crypto = require('crypto');
const MD5 = { encrypt(data) { const str = String(data); return crypto.createHash('md5').update(str).digest('hex'); },
encryptWithSalt(data, salt) { return this.encrypt(`${data}${salt}`); },
verify(data, salt, hash) { return this.encryptWithSalt(data, salt) === hash; } };
module.exports = MD5;
|
密码存储示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| async function registerUser(username, password) { const salt = crypto.randomBytes(16).toString('hex'); const hashedPassword = MD5.encryptWithSalt(password, salt);
await db.users.insert({ username, password: hashedPassword, salt, createdAt: new Date() }); }
async function verifyUser(username, password) { const user = await db.users.findOne({ username }); if (!user) return false; return MD5.verify(password, user.salt, user.password); }
|
提示:生产环境建议用bcrypt、scrypt或Argon2替代MD5。
HTTP请求处理
Axios基础
安装:
GET请求:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| const axios = require('axios');
axios.get('https://api.example.com/data') .then(response => { console.log(response.data); }) .catch(error => { console.error(error); });
axios.get('https://api.example.com/search', { params: { keyword: 'nodejs', page: 1 } });
|
POST请求:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| const postData = { app_id: 'your-app-id', open_id: 'user-open-id' };
axios.post('https://api.example.com/submit', postData) .then(response => { console.log(response.status); console.log(response.data); });
const formData = { file: fs.createReadStream('/path/to/file'), name: 'upload' };
const header = { 'content-type': 'multipart/form-data' };
axios.post('https://api.example.com/upload', formData, { headers: header });
|
创建实例:
1 2 3 4 5 6 7 8 9 10 11
| const apiClient = axios.create({ baseURL: 'https://api.example.com', timeout: 10000, headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer your-token' } });
apiClient.get('/users') .then(response => console.log(response.data));
|
请求拦截器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| apiClient.interceptors.request.use( config => { console.log('Request:', config.url); return config; }, error => { return Promise.reject(error); } );
apiClient.interceptors.response.use( response => { return response.data; }, error => { if (error.response) { console.error('Error status:', error.response.status); } return Promise.reject(error); } );
|
错误处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| async function fetchData() { try { const response = await axios.get('https://api.example.com/data'); return response.data; } catch (error) { if (error.response) { console.error('Server error:', error.response.status); console.error('Error data:', error.response.data); } else if (error.request) { console.error('No response:', error.request); } else { console.error('Error:', error.message); } } }
|
VSCode调试配置
Node.js调试
.vscode/launch.json:
1 2 3 4 5 6 7 8 9 10 11 12 13
| { "version": "0.2.0", "configurations": [ { "type": "node", "request": "launch", "name": "Launch Program", "skipFiles": ["<node_internals>/**"], "program": "${workspaceFolder}/src/main.js", "outFiles": ["${workspaceFolder}/dist/**/*.js"] } ] }
|
NestJS调试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| { "version": "0.2.0", "configurations": [ { "type": "node", "request": "launch", "name": "Launch NestJS", "skipFiles": ["<node_internals>/**"], "program": "${workspaceFolder}/src/main.ts", "preLaunchTask": "nest-build", "outFiles": [ "${workspaceFolder}/dist/**/*.js", "!**/node_modules/**" ] } ] }
|
配套tasks.json:
1 2 3 4 5 6 7 8 9 10 11 12
| { "version": "2.0.0", "tasks": [ { "label": "nest-build", "type": "shell", "command": "nest build", "problemMatcher": [], "group": "build" } ] }
|
EJS模板引擎升级
EJS 2.7.4到3.0.1语法变化:
1 2 3 4 5
| <!-- 2.7.4 旧语法 --> <% include ./header.ejs %>
<!-- 3.0.1 新语法 --> <%- include('./header.ejs') %>
|
迁移检查:
| 旧语法 |
新语法 |
<% include file %> |
<%- include('file') %> |
<% include file.ejs %> |
<%- include('file.ejs') %> |
JavaScript值判断
判断undefined
1 2 3 4 5 6 7 8 9
| if (typeof value === "undefined") { console.log("is undefined"); }
if (value === undefined) { console.log("is undefined"); }
|
判断null
1 2 3 4 5 6 7 8 9
| if (value === null) { console.log("is null"); }
if (!value && typeof value !== "undefined") { console.log("is null"); }
|
判断NaN
1 2 3 4
| if (isNaN(value)) { console.log("is NaN"); }
|
判断空值
1 2 3 4 5 6 7 8 9 10 11 12
| function isEmpty(value) { return value === null || value === undefined || (typeof value === 'string' && value.trim() === ''); }
isEmpty(null); isEmpty(undefined); isEmpty(""); isEmpty(" "); isEmpty(0); isEmpty(false);
|
判断假值
1 2 3 4 5 6 7
| if (!value) { console.log("falsy value"); }
const isFalsy = !value && value !== 0 && value !== false;
|
Object.assign
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| const target = { name: '张三' }; const source = { age: 18 }; const result = Object.assign(target, source); console.log(result); console.log(target === result);
const newObj = Object.assign({}, target, source);
const obj = Object.assign({}, obj1, obj2, obj3);
const obj1 = { name: '张三', age: 16 }; const obj2 = { age: 18 }; const merged = Object.assign({}, obj1, obj2); console.log(merged.age);
|
Node.js后端开发整体来说上手快,但坑也不少。npm依赖地狱、异步处理、内存泄漏是老问题了。建议:
- 用nvm管理Node版本
- npm换国内镜像
- 加密用内置crypto模块
- HTTP请求用Axios,拦截器很好用
- VSCode调试配置好能省很多时间
有问题的欢迎留言讨论。