前言
- 记账小程序-云开发改node后端。 因云开发免费额度取消,刚好有一个服务器一直没有用+想重新学习node
node这么多框架,怎么选
Express
- **简介**:Express 是最流行的 Node.js web 应用框架之一。它基于 Node.js 的 HTTP 模块构建,提供了简洁的 API 用于构建 web 应用和 API。它的设计理念是简单、灵活,适合快速开发各种类型的 web 服务。
- **特点**:
- **简单易用**:具有简洁的路由系统。例如,定义一个简单的 GET 请求路由可以这样写:
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});
- 中间件支持:可以方便地使用中间件来处理请求和响应。中间件函数可以在请求到达路由处理程序之前或之后执行,用于功能如日志记录、身份验证、数据解析等。例如,使用
body - parser
中间件来解析 POST 请求中的 JSON 数据:
const bodyParser = require('body - parser');
app.use(bodyParser.json());
- 高度可扩展:能够轻松地与各种数据库(如 MongoDB、MySQL 等)和模板引擎(如 EJS、Pug 等)集成,以构建功能丰富的 web 应用。
Koa
- 作为 Express 的后继者,Koa 在异步操作处理和中间件机制上更加先进,通过使用 async/await 语法,让异步代码的编写更加直观,采用洋葱模型的中间件,使请求和响应的处理更加灵活。
NestJS
- **简介**:NestJS 是一个用于构建高效、可扩展的 Node.js 服务器端应用程序的框架。它是基于 Type - Cript 构建的,充分利用了 TypeScript 的类型系统,使得代码更加健壮和易于维护。
- **特点**:
- **模块化架构**:采用模块来组织代码,每个模块可以包含控制器、服务、提供者等组件。例如,定义一个简单的模块:
import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';
@Module({
controllers: [CatsController],
providers: [CatsService],
})
export class CatsModule {}
- 依赖注入:支持依赖注入,使得组件之间的耦合度降低。服务可以被注入到控制器中,便于代码的复用和测试。例如,在控制器中注入服务:
import { Controller, Get } from '@nestjs/common';
import { CatsService } from './cats.service';
@Controller('cats')
export class CatsController {
constructor(private catsService: CatsService) {}
@Get()
async findAll() {
return this.catsService.findAll();
}
}
- 面向切面编程(AOP)支持:可以通过拦截器、过滤器等实现 AOP 功能,用于处理如日志记录、异常处理、性能监控等横切关注点。
LoopBack
- **简介**:LoopBack 是一个高度可扩展的 Node.js 框架,用于快速构建 RESTful API 和后端服务。它提供了强大的模型驱动的开发方式,能够自动生成 API 端点和数据库访问代码。
- **特点**:
- **模型驱动开发**:通过定义数据模型,LoopBack 可以自动生成 CRUD(创建、读取、更新、删除)操作的 API。例如,定义一个简单的用户模型:
const {Model, DataSource} = require('loopback');
const ds = new DataSource('memory');
class User extends Model {}
User.attachTo(ds);
User.modelName = 'User';
- 强大的连接器支持:可以连接到各种后端数据源,包括关系型数据库(如 MySQL、PostgreSQL)和非关系型数据库(如 MongoDB),以及其他外部服务(如 REST API、SOAP API)。例如,连接到 MySQL 数据库:
const {Connector, DataSource} = require('loopback');
const mysqlConnector = Connector.extend('mysql', require('loopback - connector - mysql'));
const ds = new DataSource('mysqlDS', {
connector: mysqlConnector,
host: 'localhost',
port: 3306,
database: 'mydb',
username: 'user',
password: 'password'
});
- 内置认证和授权机制:提供了用户认证和权限管理的功能,方便构建安全的 API。可以通过配置不同的认证策略(如基于 JWT、OAuth 等)来保护 API 端点。
Hapi
- **简介**:Hapi 是一个用于构建应用程序和服务的 Node.js 框架,它注重配置和插件系统,能够构建稳定、安全的 web 服务。
- **特点**:
- **配置驱动开发**:使用配置对象来定义服务器设置、路由、插件等。例如,定义一个简单的路由配置:
const Hapi = require('hapi');
const server = new Hapi.Server({
port: 3000,
host: 'localhost'
});
server.route({
method: 'GET',
path: '/',
handler: (request, h) => {
return 'Hello World';
}
});
- 强大的插件系统:支持插件的开发和使用,插件可以用于扩展服务器的功能,如日志记录、缓存、认证等。可以方便地安装和卸载插件,并且插件之间相互独立,便于维护和升级。例如,使用
hapi - vision
插件来支持模板引擎:
const Inert = require('hapi - inert');
const Vision = require('hapi - vision');
const server = new Hapi.Server({...});
await server.register([
Inert,
Vision
]);
为什么选Koa
为什么选Koa
- 异步操作处理优势
- 中间件机制的灵活性(洋葱模型)
- 轻量级和可定制性
缺点:轻量级过于灵活,缺少内置功能 想怎么写就怎么写。。。
ps:这里2年前用koa写的了,现在公司在使用NestJS,所有后续还会用NestJS重构一下,体验不同框架之间的差别和优劣、开发体验等等
开发微信小程序(记账小程序&h5通用api)
- 为什么写记账功能,因为功能简单明了,容易上手。
- 为什么要记账,你不理财财不理你
- 记住普通人的第一笔启动资金就是攒钱攒出来的,大钱不是攒来的,小钱就是靠我们一点一点的积累才有机会去尝试更丰富的财富路径。不要相信哪些营销号消费陷阱:消费升级、提前消费、钱换一种方式留在身边、不花钱就不会赚钱,这就是裹挟消费绑架等等。 攒钱不是吝啬,可以投资自己提升自己
需求规划:
- 首页(banner、功能区、意见反馈入口)小程序登录授权&邮箱登录&手机号登录
- 添加/编辑账单(涉及收入/支出、消费类型、时间、金额、备注等等)api
- 新增自定义消费类型api
- 账单列表api
- 账单统计表格(类型分类、账本、时间筛选、收入/支出/总揽排行)
- 预设多记账本功能(待开发)
当时计划的TODOlist
- koa过于简单,需要二次开发
- 处理body传参 koa-bodyparser
- 通过文件动态创建路由 require-directory
- 请求参数检验 joi
- 全局错误处理
- jwt token 处理
- sequelize 操作数据库
- mysql
- redis 缓存
- 静态文件 koa-static
- 请求 axios
- 上传 koa-multer
- 本地热更新 nodemon
基于Koa二次开发
app.js
- 自动建标表
- 全局错误处理
- 静态资源
- 异步 Promise
今天先讲讲全局错误处理和异步 Promise InitManager.initCore(app)
require('module-alias/register')
const Koa = require('koa');
const path = require('path');
const bodyParser = require('koa-bodyparser');
const static = require('koa-static');
const catchError = require('@middlewares/exception');
const InitManager = require('@core/init');
// 自动建标表
require('@models/index');
const app = new Koa();
// 全局错误处理
app.use(catchError);
// 使用ctx.body解析中间件 ctx.request.body
app.use(bodyParser());
// 静态资源
app.use(static(path.join(__dirname, './static')))
// 注册 中间键 上下文ctx 洋葱模型
// 异步 Promise
InitManager.initCore(app);
app.listen(3000, () => {
console.log('启动 3000')
});
全局错误处理
洋葱模型在这里就不多赘述了,后面有机会单独出一篇文章,这里先应用
const catchError = require('@middlewares/exception');
const app = new Koa();
// 全局错误处理
app.use(catchError);
catchError middlewares/exception.js
统一处理错误信息 msg、errorCode、request等等 这里还可以加上traceId 请求唯一值查询日志等等
const { HttpException } = require('../core/http-exception')
const catchError = async (ctx, next) => {
try {
await next();
} catch (error) {
const isHttpException = error instanceof HttpException;
const isDev = global.config.environment === 'dev';
if (isDev && !isHttpException) {
throw error;
}
if (error instanceof HttpException) {
const { msg, errorCode, code } = error;
ctx.body = {
msg: msg,
error_code: errorCode,
request: `${ctx.method} ${ctx.path}`
};
ctx.status = code;
} else {
ctx.body = {
msg: 'we made a mistake',
error_code: 999,
request: `${ctx.method} ${ctx.path}`
};
ctx.status = 500;
}
}
}
module.exports = catchError;
core/http-exception
这个构造函数的目的是创建一个特定的错误对象,用于在应用程序中表示资源未找到的情况。通过继承 HttpException
,它可以与其他 HTTP 异常类一起使用,提供统一的错误处理机制。
class HttpException extends Error{
constructor(msg = '服务器异常', errorCode = 10000, code = 400) {
super();
this.errorCode = errorCode;
this.code = code;
this.msg = msg;
}
}
class ParameterException extends HttpException {
constructor(msg, errorCode) {
super();
this.code = 400;
this.msg = msg || '参数错误';
this.errorCode = errorCode || 10000;
}
}
class Success extends HttpException {
constructor(msg, errorCode) {
super();
this.code = 200;
this.msg = msg || 'ok';
this.errorCode = errorCode || 0;
}
}
class NotFound extends HttpException{
constructor(msg, errorCode) {
super();
this.msg = msg || '资源未找到';
this.errorCode = errorCode || 10000;
this.code = 404;
}
}
class AuthFailed extends HttpException{
constructor(msg, errorCode) {
super();
this.msg = msg || '授权失败';
this.errorCode = errorCode || 10004;
this.code = 401;
}
}
class Forbidden extends HttpException {
constructor(msg, errorCode) {
super();
this.msg = msg || '禁止访问';
this.errorCode = errorCode || 10006;
this.code = 403;
}
}
module.exports = {
HttpException,
ParameterException,
Success,
NotFound,
AuthFailed,
Forbidden,
};
初始化initCore
init.js
- 读取配置(服务&小程序appid等等信息s)
- 基于require-directory 通过文件夹路径生成api
- 配置http全局global.errs
const Router = require('koa-router');
const requireDirectory = require('require-directory');
class InitManager {
static initCore(app) {
InitManager.app = app
InitManager.initLoadRouters();
InitManager.loadHttpException();
InitManager.loadConfig();
}
static loadConfig(path = '') {
const configPath = path || process.cwd() + '/config/config.js'
const config = require(configPath)
global.config = config
}
static initLoadRouters() {
const path = `${process.cwd()}/app/api/`
const visitor = function (obj) {
if (obj instanceof Router) {
InitManager.app.use(obj.routes())
}
}
requireDirectory(module, path, { visit: visitor })
}
static loadHttpException() {
const errors = require('./http-exception')
global.errs = errors
}
}
module.exports = InitManager;
开始工作已完成
后续就是编写api了
未完待续,koa开发持续更新中...
感谢关注点赞评论~