基于koa,自定义简单的MVC项目结构

定义路由实例,提供统一管理路由的工厂函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// 定义MyRouter实例
import * as Router from 'koa-router';
import * as Koa from 'koa';
import * as compose from 'koa-compose';

class MyRouter<T> {
private routes: Router<Koa.Context, T>[] = [];
public export: Koa.Middleware;

public get: Router<Koa.Context, T>['get'];
public post: Router<Koa.Context, T>['post'];
public delete: Router<Koa.Context, T>['delete'];
public put: Router<Koa.Context, T>['put'];
public link: Router<Koa.Context, T>['link'];
public unlink: Router<Koa.Context, T>['unlink'];
public patch: Router<Koa.Context, T>['patch'];
public all: Router<Koa.Context, T>['all'];

public constructor() {
const router = new Router<Koa.Context, T>();
this.routes.push(router);

this.get = router.get.bind(router);
this.post = router.post.bind(router);
this.delete = router.delete.bind(router);
this.put = router.put.bind(router);
this.link = router.link.bind(router);
this.unlink = router.unlink.bind(router);
this.patch = router.patch.bind(router);
this.all = router.all.bind(router);

Object.defineProperty(this, 'export', {
get() {
const result: any[] = [];
this.routes.forEach((route: Router) => {
result.push(route.routes(), route.allowedMethods());
});
return compose(result);
}
});
}

public prefix(str: string) {
const router = new Router<Koa.Context, T>({ prefix: str });
this.routes.push(router);
return router;
}
}

export default MyRouter;

定义扩展的Context的TypeScript类型描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// types
import * as Router from 'koa-router';
import * as Koa from 'koa';
import 'koa-body';
import { Connection } from 'mongoose';
import { Sequelize } from 'sequelize';
import { Redis } from 'ioredis';

// 这个类型,提供给实例化工厂路由使用
export interface ExtraContext {
mongodb: Connection;
mysql: Sequelize;
redis: Redis;
currentTime: number;
session: {
uid: number;
};
}

// 这个类型提供给控制器使用
export type Ctr = Router.IMiddleware<Koa.Context, ExtraContext>;

定义控制器,编写核心业务代码

一般创建文件夹controllers/user/index.js

1
2
3
4
export const getUserInfo: Ctr = async (ctx) => {
// 处理业务逻辑
ctx.body = { ret: 0, data: {} };
}

定义路由,提供统一管理能力

一般直接创建routes.js即可,您也只需一个路由定义文件即可,便于对于路由的统一管理

1
2
3
4
5
6
7
8
9
10
11
12
13
import * as UserCtr from './controllers/user';

const router = new MyRouter<ExtraContext>();

router
.prefix('/api/user')
.get('/info', UserCtr.getUserInfo);

router
.prefix('/api/project')
.get('/detail',ProjectCtr.getProjectDetail)

export default router.export