MidwayJS 全栈开发(二)HelloWorld实战

MidwayJS 全栈开发(二)HelloWorld实战

 

 

 

前言

上一篇内容回顾:# MidwayJS 全栈开发(一)技术选型

本篇:通过编写一个 HelloWorld 应用,串一下后端开发流程。


初始化项目

这里我们可以通过官方指引完成项目初始化和启动,我们按 官网脚手架 初始化生成项目,执行 npm run dev 启动应用即可(自带代码更新重启)。

略略略。


项目结构

我们只关注 ./src 目录下的内容,里面包括文件如下:

./src
├── config                // 配置文件目录
├── config.default.ts
└── config.unittest.ts
├── configuration.ts      // 应用入口文件
├── controller            // 控制器目录,管理路由
├── api.controller.ts
└── home.controller.ts
├── filter                // 过滤器目录,先不看
├── default.filter.ts
└── notfound.filter.ts
├── interface.ts          // 全局TS类型定义文件
├── middleware            // 中间件目录,先不看
└── report.middleware.ts
└── service               // 服务目录,编写CRUD逻辑
    └── user.service.ts

HelloWord 实战

这里按照个人的开发习惯重组一下代码,借此大家可以更深入了解下 Midway 项目大致组织结构,也方便后续教程进行。

这里推荐大家使用 VsCode 编辑器进行开发。

修改配置

koa 里的配置对象是基于约定的,最终会传给底层依赖的 Koa 框架。我们可以尝试修改,看看效果。

1. 端口号

找到 ./src/config/config.default.ts 文件,修改端口号 7001 为 8080 或者其他。

2. 全局路由前缀

同样,可以添加 globalPrefix 字段设置全局路由的前缀。

// ./src/config/config.default.ts
import { MidwayConfig } from '@midwayjs/core';

export default {
  // use for cookie sign key, should change to your own and keep security
  keys: '1675590443187_6035',
  koa: {
    port: 8080,
    globalPrefix: "/api",
  },
} as MidwayConfig;

之后只要请求服务,所有的路由都要加上 api 前缀。

 

globalPrefix-image

 

这里推荐大家使用 ApiFox 进行API请求和调试。

新增模块

关于目录结构,个人习惯于以目录的形式聚合相关联 controller 和 service,方便聚焦管理和查找。

  • Controller 主要负责路由的管理以及入参出参的校验
  • Service 主要负责业务逻辑和 CRUD。两者需要划分边界,以此开后续代码的可维护性

这里我们以创建 get_user(根据 id 获取用户信息) API 为例。

1. 新增 Service

新增 ./src/modules/user 目录,在 user 目录下创建 user.service.ts 文件。

import { Provide } from '@midwayjs/decorator';

@Provide()
export class UserService {
  async get(uid: string) {
    if (!uid) return null;

    // mock 数据库查询返回数据
    return {
      uid: uid,
      username: '不败花',
      phone: '12345678901',
      email: 'xxxxxx@xxx.com',
    };
  }
}

2. 新增 Controller

再在 user 目录下创建 user.controller.ts 文件。请求API路径为 /api/user/get_user

import { Controller, Get, Inject, Query } from '@midwayjs/decorator';
import { UserService } from './user.service';

// 路由
@Controller('/user')
export class UserController {
  @Inject()
  userService: UserService;

  @Get('/get_user')
  async getUser(@Query('uid') uid) {
    const user = await this.userService.get(uid);

    if (!user) return { code: 6000, msg: 'User not found', data: null };

    return { code: 0, msg: 'OK', data: user };
  }
}

@Get 表示以 GET 方式接收请求,通过 @Query('uid') 来获取请求查询参数 uid

最后,我们用 Apifox 测试验证一下请求,传入任意请求参数 uid

 

image.png

 

可以看到,API 请求是可以成功的。这里我们并没有使用将 UserController 在任何地方配置引入,路由就可以生效。这是因为我们通过 @Controller('/user') 实现了路由注册,MidwayJS 默认在 src 目录下 @Controller 声明的都会被注册为 API 路由,最终生效。这点可能和 NestJS 不大相同(需要最终在根模块上声明)。

小结

至此,对于获取用户的 API,我们的整个开发流程已经完成的七七八八了。

可以看到,对于搬砖工作,主要还是集中在 Service 和 Controller 里面,所以我更倾向于聚合在一个文件夹下面,不至于太乱。


响应结构一致性

对于 Rest API 的响应体设计,我们可以看到,大多数是和前端约定好的统一的数据结构,这样前端对接可以做相应的逻辑处理;同时,约定统一的接口响应规范也可以减少重复编码,提高团队整体效率。

值得一提的是,团队基于此可以做很多事情,比如埋点统一读取字段上报统计分析、接口文档统一导出、请求库统一封装等等。

{ code: number, msg: string, data: any }

1. 新增辅助类

这里我们后端一般会抽象一个工具类来实现响应结构统一,以后都可以使用这个辅助类进行接口出参处理。

// ./src/utils/index.ts
export class BaseResponse<T> {
  code: number;
  msg: string;
  data: T;

  constructor(data: T, code: number, msg: string) {
    this.code = code;
    this.msg = msg;
    this.data = data;
  }

  static create<T>(data: T, code: number, msg: string) {
    return new BaseResponse(data, code, msg);
  }

  static ok<T>(data: T) {
    return BaseResponse.create(data, 0, 'OK');
  }

  static error(msg: string, code: number) {
    return BaseResponse.create(null, code, msg);
  }
}

2. BaseResponse 改造

然后,我们回到 user.service.ts 进行一波改造,这样就可以愉快的玩耍啦。

import { Controller, Get, Inject, Query } from '@midwayjs/decorator';
+ import { BaseResponse } from '../../utils';
import { UserService } from './user.service';

// 路由
@Controller('/user')
export class AppController {
  @Inject()
  userService: UserService;

  @Get('/get_user')
  async getUser(@Query('uid') uid) {
    const user = await this.userService.get(uid);

-   if (!user) return { code: 6000, msg: 'User not found', data: null };
+    if (!user) return BaseResponse.error('User not found', 6000);

-    return { code: 0, msg: 'OK', data: user };
+    return BaseResponse.ok(user);
  }
}

小结

主要讲了 BaseResponse 的优化,帮助我们统一前后端接口基础规范。


全文总结

本篇主要以 Midway 作为入手,完成了项目从零到 HelloWorld 的过程实践。可以看到,对于 RestAPI 项目,主要聚焦在 Service(负责逻辑处理)和 Controller(负责路由管理)两块的实现,使用上还是比较简单的。

当然对于装饰器的原理以及依赖注入,这里不做深入剖析,感兴趣的老友可以了解看看。

发布于 2023-02-06 11:29・IP 属地上海

posted on 2024-04-19 16:45  漫思  阅读(15)  评论(0编辑  收藏  举报

导航