Chap05-NodejsServer
Chap05-NodejsServer
Nodejs基础
非专业,仅因使用在这里补充一些简单的基础。
1. JavaScript 基础语法
变量声明
javascript
// var (函数作用域,不推荐)
var oldVariable = "value";
// let (块级作用域,可重新赋值)
let counter = 0;
counter = 1;
// const (块级作用域,不可重新赋值)
const PI = 3.14159;
const user = { name: "John" };
user.name = "Jane"; // 允许,对象内部可修改
数据类型
javascript
// 原始类型
const str = "Hello";
const num = 42;
const bool = true;
const nullValue = null;
const undefinedValue = undefined;
const sym = Symbol("unique");
// 引用类型
const obj = { key: "value" };
const arr = [1, 2, 3];
const func = function() { return "result"; };
函数
javascript
// 函数声明
function add(a, b) {
return a + b;
}
// 函数表达式
const multiply = function(a, b) {
return a * b;
};
// 箭头函数 (ES6+)
const divide = (a, b) => a / b;
const square = x => x * x;
// 默认参数
function greet(name = "Guest") {
return `Hello, ${name}!`;
}
2. 对象和数组操作
对象
javascript
// 对象创建和访问
const person = {
name: "Alice",
age: 30,
hobbies: ["reading", "coding"],
// 方法
introduce() {
return `I'm ${this.name}, ${this.age} years old`;
}
};
// 动态访问
console.log(person.name); // "Alice"
console.log(person["age"]); // 30
// 对象解构
const { name, age } = person;
const { name: personName, ...rest } = person;
// 对象扩展
const updatedPerson = { ...person, city: "New York" };
数组
javascript
// 数组操作
const numbers = [1, 2, 3, 4, 5];
// 常用方法
numbers.push(6); // 末尾添加
numbers.pop(); // 末尾删除
numbers.map(x => x * 2); // 映射新数组
numbers.filter(x => x > 2);// 过滤
numbers.reduce((sum, x) => sum + x, 0); // 累加
// 数组解构
const [first, second, ...others] = numbers;
// 扩展运算符
const newArray = [...numbers, 6, 7];
3. 控制流
条件语句
javascript
// if-else
if (age >= 18) {
console.log("Adult");
} else if (age >= 13) {
console.log("Teenager");
} else {
console.log("Child");
}
// 三元运算符
const status = age >= 18 ? "Adult" : "Minor";
// switch
switch (day) {
case "Monday":
console.log("Start of week");
break;
case "Friday":
console.log("Weekend coming");
break;
default:
console.log("Regular day");
}
循环
javascript
// for 循环
for (let i = 0; i < 5; i++) {
console.log(i);
}
// for...of (数组)
for (const item of array) {
console.log(item);
}
// for...in (对象属性)
for (const key in object) {
console.log(key, object[key]);
}
// while
let i = 0;
while (i < 5) {
console.log(i);
i++;
}
// 数组方法循环
array.forEach(item => console.log(item));
4. 类与面向对象
javascript
// 类定义
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
// 方法
introduce() {
return `Hello, I'm ${this.name}`;
}
// 静态方法
static createAnonymous() {
return new Person("Anonymous", 0);
}
}
// 继承
class Student extends Person {
constructor(name, age, grade) {
super(name, age); // 调用父类构造函数
this.grade = grade;
}
study() {
return `${this.name} is studying`;
}
}
// 使用
const student = new Student("Bob", 20, "A");
console.log(student.introduce());
5. 异步编程核心
Promise
javascript
// 创建 Promise
const fetchData = new Promise((resolve, reject) => {
setTimeout(() => {
const success = Math.random() > 0.5;
if (success) {
resolve("Data received");
} else {
reject("Error: Failed to fetch data");
}
}, 1000);
});
// 使用 Promise
fetchData
.then(data => {
console.log("Success:", data);
return processData(data);
})
.then(processedData => {
console.log("Processed:", processedData);
})
.catch(error => {
console.error("Error:", error);
})
.finally(() => {
console.log("Operation completed");
});
// Promise 工具方法
Promise.all([promise1, promise2]) // 所有成功
Promise.race([promise1, promise2]) // 第一个完成
Promise.any([promise1, promise2]) // 第一个成功
Async/Await
javascript
// async 函数总是返回 Promise
async function getUserData(userId) {
try {
const user = await fetchUser(userId);
const posts = await fetchUserPosts(userId);
return {
user,
posts
};
} catch (error) {
console.error("Failed to get user data:", error);
throw error; // 重新抛出错误
}
}
// 使用 async 函数
getUserData(123)
.then(data => console.log(data))
.catch(error => console.error(error));
// 立即执行 async 函数
(async () => {
try {
const data = await getUserData(123);
console.log(data);
} catch (error) {
console.error(error);
}
})();
6. 错误处理
javascript
// try-catch
try {
// 可能抛出错误的代码
const result = riskyOperation();
console.log(result);
} catch (error) {
console.error("Operation failed:", error.message);
} finally {
console.log("Cleanup");
}
// 自定义错误
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = "ValidationError";
}
}
// 抛出错误
function validateInput(input) {
if (!input) {
throw new ValidationError("Input cannot be empty");
}
}
// Promise 错误处理
someAsyncFunction()
.catch(error => {
console.error("Async error:", error);
return defaultValue;
});
7. 模块系统
CommonJS 模块 (Node.js 默认)
javascript
// math.js - 导出模块
const add = (a, b) => a + b;
const multiply = (a, b) => a * b;
// 导出方式 1: module.exports
module.exports = {
add,
multiply
};
// 导出方式 2: exports (不推荐直接赋值)
exports.add = add;
exports.multiply = multiply;
// app.js - 导入模块
const math = require('./math.js');
// 或者解构导入
const { add, multiply } = require('./math.js');
console.log(math.add(2, 3)); // 5
ES6 模块 (需要 package.json 中 "type": "module")
javascript
// math.mjs - 导出
export const add = (a, b) => a + b;
export const multiply = (a, b) => a * b;
export default function calculator() { /* ... */ }
// app.mjs - 导入
import calculator, { add, multiply } from './math.mjs';
import * as math from './math.mjs';
8. 事件处理
javascript
const EventEmitter = require('events');
// 创建事件发射器
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
// 监听事件
myEmitter.on('event', (data) => {
console.log('Event received:', data);
});
// 一次性监听
myEmitter.once('firstEvent', () => {
console.log('This will only run once');
});
// 发射事件
myEmitter.emit('event', { message: 'Hello' });
myEmitter.emit('firstEvent');
// 错误事件处理
myEmitter.on('error', (error) => {
console.error('Emitter error:', error);
});
9. 函数高级特性
闭包
javascript
function createCounter() {
let count = 0; // 私有变量
return {
increment: () => ++count,
decrement: () => --count,
getCount: () => count
};
}
const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.getCount()); // 1
高阶函数
javascript
// 接受函数作为参数
function mapArray(arr, transform) {
const result = [];
for (let item of arr) {
result.push(transform(item));
}
return result;
}
const doubled = mapArray([1, 2, 3], x => x * 2);
// 返回函数
function createMultiplier(multiplier) {
return function(x) {
return x * multiplier;
};
}
const double = createMultiplier(2);
console.log(double(5)); // 10
10. 实用语法特性
模板字符串
javascript
const name = "Alice";
const age = 30;
// 多行字符串和插值
const message = `
Hello, ${name}!
You are ${age} years old.
Next year you'll be ${age + 1}.
`;
console.log(message);
可选链和空值合并
javascript
const user = {
profile: {
name: "John",
address: {
city: "New York"
}
}
};
// 可选链 (?.)
const city = user?.profile?.address?.city; // "New York"
const country = user?.profile?.address?.country; // undefined
// 空值合并 (??)
const displayName = user?.name ?? "Anonymous";
const count = 0;
const result = count ?? 42; // 0 (只有 null/undefined 才用默认值)
解构赋值
javascript
// 数组解构
const [a, b, ...rest] = [1, 2, 3, 4, 5];
// a=1, b=2, rest=[3,4,5]
// 对象解构
const { name, age, ...otherProps } = person;
const { name: personName, age: personAge } = person;
// 函数参数解构
function printUser({ name, age = 18 }) {
console.log(`${name} is ${age} years old`);
}
11. 内存管理注意事项
javascript
// 避免内存泄漏
class DataProcessor {
constructor() {
this.data = new Array(1000000).fill('data');
// 移除不必要的全局引用
}
// 清理方法
cleanup() {
this.data = null;
}
}
// 使用弱引用 (WeakMap/WeakSet)
const weakMap = new WeakMap();
const key = {};
weakMap.set(key, 'value');
// 当 key 被垃圾回收时,值也会自动清理
Nodejs实现邮箱验证服务
这一节我们使用nodejs实现邮箱发送服务。之所以不用cpp实现,是因为相对来说要复杂的多。因此,我们选用了易用的nodejs.
首先选择一个目录,使用npm init初始化项目。
然后清除旧的镜像源
npm cache clean --force
重新设置淘宝镜像源
npm config set registry https://registry.npmmirror.com
紧接着下载grpc-js
npm install @grpc/grpc-js
安装proto-loader解析proto文件
npm install @grpc/proto-loader
再来安装重要的流行的
npm install node nodemailer
整体项目如下

首先我们把在GateWayServer创建的proto文件移动到这里。因为既然要通信,那么通信的载体或者说格式应该是双方互通的。在这里我们不需要命令工具解析这个proto文件,因为我们使用js库动态解析。
-
proto.js
用于动态解析proto文件。
const path = require('path') const grpc = require('@grpc/grpc-js') const protoLoader = require('@grpc/proto-loader') const PROTO_PATH = path.join(__dirname, 'message.proto') const packageDefinition = protoLoader.loadSync(PROTO_PATH, { keepCase: true, longs: String, enums: String, defaults: true, oneofs: true }) const protoDescriptor = grpc.loadPackageDefinition(packageDefinition) const message_proto = protoDescriptor.message module.exports = message_proto -
config.json
实现参数方便配置,我们创建json文件存入配置方便修改。
{ "email":{ "user" : "v125250@163.com", "pass" : "YJn8bUJKDLfEGA9F" }, "mysql":{ "host":"127.0.0.1", "port":33046, "password":38324 }, "redis":{ "host":"127.0.0.1", "port":6380, "password" :38324 } } -
config.js
有了json配置,我们创建config.js来读取配置
const fs = require('fs'); let config = JSON.parse(fs.readFileSync('config.json', 'utf8')); let email_user = config.email.user; let email_pass = config.email.pass; let mysql_host = config.mysql.host; let mysql_port = config.mysql.port; let redis_host = config.redis.host; let redis_port = config.redis.port; let redis_passwd = config.redis.passwd; let code_prefix = "code_"; module.exports = {email_pass, email_user, mysql_host, mysql_port,redis_host, redis_port, redis_passwd, code_prefix} -
email.js
封装发邮件的模块+发邮件的方法
const nodemailer = require('nodemailer'); const config_module = require("./config") /** * 创建发送邮件的代理 */ let transport = nodemailer.createTransport({ host: 'smtp.163.com', port: 465, secure: true, auth: { user: config_module.email_user, // 发送方邮箱地址 pass: config_module.email_pass // 邮箱授权码或者密码 } }); /** * 发送邮件的函数 * @param {*} mailOptions_ 发送邮件的参数 * @returns */ /** 因为transport.SendMail相当于一个异步函数,调用该函数后发送的结果是通过回调函数通知的,所以我们没办法同步使用,需要用Promise封装这个调用,抛出Promise给外部,那么外部就可以通过await或者then catch的方式处理了。 这里的Promise类似cpp的future包装了一下,future.wait可以同步的等待接受数据。 这里的promise同理。 */ function SendMail(mailOptions_){ return new Promise(function(resolve, reject){ transport.sendMail(mailOptions_, function(error, info){ if (error) { console.log(error); reject(error); } else { console.log('邮件已成功发送:' + info.response); resolve(info.response) } }); }) } module.exports.SendMail = SendMail -
server.js
这个文件是主main入口。
显示引用了需要的模块,然后为grpc服务器添加GetSecurityCode服务。这个GetSecurityCode的方法名称需要和proto定义的一致。
同时为了接收方接受的界面美观,我们在html_str使用了样式美化。
在要传送的结构mailOptions里面传给html即可。或者如果要使用纯文本,传给text也可。
const grpc = require('@grpc/grpc-js') const message_proto = require('./proto') const const_module = require('./const') const {v4:uuidv4} = require('uuid') const emailModule = require('./email') async function GetSecurityCode(call, callback) { console.log("Email is:", call.request.email) try{ uniqueId = uuidv4().toUpperCase().substring(0,4); console.log("UniqueId is ", uniqueId) let html_str = ` <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <style> .container { max-width: 600px; margin: 0 auto; font-family: Arial, sans-serif; } .header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 20px; text-align: center; } .content { padding: 30px; background: #f8f9fa; } .code-box { background: white; padding: 25px; border-radius: 10px; text-align: center; margin: 20px 0; box-shadow: 0 4px 6px rgba(0,0,0,0.1); } .verification-code { font-size: 42px; font-weight: bold; color: #e74c3c; letter-spacing: 8px; margin: 15px 0; } .warning { color: #e74c3c; font-weight: bold; background: #ffeaa7; padding: 10px; border-radius: 5px; margin: 15px 0; } .footer { text-align: center; color: #666; font-size: 12px; padding: 20px; } </style> </head> <body> <div class="container"> <div class="header"> <h1>QuickChat 账户注册验证</h1> </div> <div class="content"> <p>亲爱的用户,您好!</p> <p>您正在注册QuickChat账户,请使用以下验证码完成验证:</p> <div class="code-box"> <p>验证码</p> <div class="verification-code">${uniqueId}</div> <p style="color: #666;">(有效期3分钟)</p> </div> <div class="warning"> ⚠️ 安全提示:请勿向任何人泄露此验证码! </div> <p>如果这不是您的操作,请忽略此邮件。</p> </div> <div class="footer"> <p>系统自动发送,请勿回复</p> </div> </div> </body> </html> `; //发送邮件 let mailOptions = { from: 'v125250@163.com', to: call.request.email, subject: '验证码', html: html_str, }; let send_res = await emailModule.SendMail(mailOptions); console.log("Send Result Is ", send_res) callback(null, { email: call.request.email, error:const_module.Errors.Success }); }catch(error){ console.log("Catch Error Is ", error) callback(null, { email: call.request.email, error:const_module.Errors.Exception }); } } function main() { var server = new grpc.Server() server.addService(message_proto.VarifyService.service, { GetSecurityCode: GetSecurityCode }) server.bindAsync('0.0.0.0:50051', grpc.ServerCredentials.createInsecure(), () => { server.start() console.log('grpc server started') }) } main()
这下,真正的邮箱发送服务完成。我们可以打开qt前端进行测试。

发送获取服务后,我们的GateWayServer接受到了请求:

同时nodejs后端服务也受到了grpc请求:

打开邮箱验证:

至此,验证码获取服务完成。

浙公网安备 33010602011771号