对于做前端开发的同学来说,多少对服务端的知识有些了解,今天就带大家加深下nodejs服务端框架 Midwayjs一些介绍和开发案例。
-
Midway <- egg.js <- koa.js, midway 底层还是封装的koa.js
MVC 架构优点****分离关注点:MVC 架构将数据处理、用户界面和控制流程分离,使得开发、测试和维护更为简单。代码复用:由于 MVC 架构的分离性,可以更容易地在不同的接口或平台上复用代码。并行开发:开发者可以在模型、视图和控制器上进行并行开发,提高了开发效率。灵活性:MVC 架构允许开发者更改视图层而不影响模型层,这为用户界面的更新和改进提供了便利。控制流程清晰:MVC 架构明确了控制流程,使得代码的阅读和理解更为容易
-
多编程范式
-
面向对象(OOP + Class + IoC):面向对象,类,容器
// src/controller/index.tsimport { Controller, Get } from '@midwayjs/core';import { Context } from '@midwayjs/koa'; @Controller('/')export class HomeController { @Inject() ctx: Context @Get('/') async home() { return { message: 'Hello Midwayjs!', query: this.ctx.ip } } -
函数式(FP + Function + Hooks)
// src/api/index.ts import { useContext } from '@midwayjs/hooks'import { Context } from '@midwayjs/koa'; export default async function home () { const ctx = useContext<Context>() return { message: 'Hello Midwayjs!', query: ctx.ip }} -
@Controller() 装饰器
-
控制器文件一般来说在
src/controller目录中,我们可以在其中创建控制器文件。Midway 使用@Controller()装饰器标注控制器,其中装饰器有一个可选参数,用于进行路由前缀(分组),这样这个控制器下面的所有路由都会带上这个前缀。 -
登录获取请求参数示例在路由层获取请求参数,如下import { Controller, Get, App, Post, Inject } from '@midwayjs/core';import { Application, Context } from '@midwayjs/koa';const url = require('url');const querystring = require('querystring'); const todoList = [];@Controller('/formapi')export class HomeController { @Inject() ctx: Context @App() app: Application; // POST /api/todo @Post('/todo') async addTodo() { let parsedUrl = url.parse(this.ctx.url); let parsedQuery = querystring.parse(parsedUrl.query); todoList.push(parsedQuery); return 'ok'; } // GET /api/todo @Get('/todo') async getTodo() { return todoList; }} -
Service 层,依赖注入
-
在业务中,只有控制器(Controller)的代码是不够的,一般来说会有一些业务逻辑被抽象到一个特定的逻辑单元中,我们一般称为服务(Service)。
-
比如我们在登录的时候做一些(复杂)操作,我们在service层做一些复杂的业务操作。
-
示例举例我们每一个用户添加uuid以及登录时间。 -
在servie层写业务逻辑,使用
@provice装饰器进行抛出,这里options可以拿到 controller 层传过来的值import { Provide } from '@midwayjs/core';import { v4 as uuidv4 } from 'uuid'; @Provide()export class LoginService { async getUser(options: any) { console.log(options, 'options'); // TODO: 登录逻辑,这里返回一个uuid return { uid: uuidv4(), loginTime: new Date().getTime(), }; }}修改controller层,使用 @Inject()装饰器进行注入,此时环境变量中就有loginService 变量可直接调用,service拿到值后进行处理后返回。
``` @Inject() loginService: LoginService; @Post('/todo') async addTodo() { let parsedUrl = url.parse(this.ctx.url); let parsedQuery = querystring.parse(parsedUrl.query); let obj =await this.loginService.getUser(parsedQuery); todoList = {...parsedQuery, ...obj}; return 'ok'; } ```通过访问接口返回值:
-
异常处理
-
普通异常定义在普通业务代码中自定义通用异常,一般很少用。
// src/error/custom.error.ts// 自定义业务异常import { HttpStatus, MidwayHttpError } from '@midwayjs/core'; export class CustomHttpError extends MidwayHttpError { constructor() { super('error', HttpStatus.BAD_REQUEST); }} 业务代码 console.log(options, 'options'); if (options?.username!== 'admin' || options?.password!== '123456') { // throw new MidwayHttpError('my custom error', 23); throw new CustomHttpError(); } -
全局异常定义全局配置异常,比如404,503等, 使用@Catch 装饰器。
-
// src/filter/internal.filter.ts
import { Catch, httpError, MidwayHttpError } from '@midwayjs/core';import { Context } from '@midwayjs/koa'; @Catch(httpError.NotFoundError)export class NotFoundError { async catch(err: MidwayHttpError, ctx: Context) { console.log(err); // ... return 'got 404 error, ' + err.message; }} // configuration.tsimport { NotFoundError } from './filter/login.filter' async onReady() { // add middleware this.app.useMiddleware([ReportMiddleware]); // add filter // this.app.useFilter([NotFoundFilter, DefaultErrorFilter]); this.app.useFilter([NotFoundError]); } -
链接数据库和Redis
-
-
安装依赖包
npm i @midwayjs/typeorm@3 typeorm --save // mysql依赖npm install mysql --savenpm install mysql2 --save // redis缓存npm i @midwayjs/redis@3 --save -
-
配置文件设置
// configuration.ts import * as orm from '@midwayjs/typeorm';import * as redis from '@midwayjs/redis'; @Configuration({ imports: [ // ... orm, redis // 加载 typeorm 组件 ], importConfigs: [ join(__dirname, './config') ]})
其中数据库需创建Model 实体类,使用@Entity()
// redis配置
redis: { client: { port: xxxxx, // Redis port host: "xxxxx", // Redis host password: "xxxxx", db: 0, }, },
// 缓存配置直接调用@Inject() redisService: RedisService; @Get('/captcha') async getCaptcha(){ let result = await this.redisService.get('ecp:platform:shop:access_token:11548297'); console.log(result, 'result'); // result = Buffer.from( result || '', 'base64').toString() return result; }