說(shuō)明
封裝 lenneth 旨在快速方便的搭建出一個(gè) node web 應(yīng)用,不過(guò)度封裝也不隨波逐流院喜,koa 的 node 是簡(jiǎn)單的肩民,lenneth 也是蔽豺。
基于 ES6+typescript 的一些特性,做了一些類似 spring 的注解棘利,對(duì)開發(fā)本身不增加復(fù)雜度橱野,并且不失 koa 的靈活性。
lenneth 內(nèi)部已經(jīng)集成了 koa, koa-bodyparser, koa-router 這 3 個(gè)基礎(chǔ)庫(kù)善玫,已經(jīng)滿足了大部分的開發(fā)水援,如果需要引入其他的中間件,可以在入口文件中引入茅郎。
lenneth 抽象了 3 個(gè)模塊蜗元,分別是 controller,middleware,service,內(nèi)部不接入任何 db 和 cache系冗。
安裝
yarn add lenneth
# or
npm install lenneth
瞄一眼
import { LennethApplication, ServerSettings, ILenneth } from "lenneth";
@ServerSettings({
port: 8081
})
class App extends LennethApplication implements ILenneth {
$onMountingMiddlewares() {
this.use(async (ctx, next) => {
ctx.body = "hello world";
});
}
}
new App().start();
open in browser
http://localhost:8081
各個(gè)模塊
- application
入口文件處奕扣,使用 ServerSettings 修飾,里面的參數(shù)都是一些全局方法毕谴,如 interceptor成畦,response 等,這些都是一個(gè) middleware涝开,lenneth 只是依照 koa 的洋蔥模型調(diào)整了他們的執(zhí)行順利
@ServerSettings({
port: 8081,
// controller
imports: {
"/apis": UserController
},
// 攔截器
interceptor: Interceptor,
// 返回值封裝
response: AppResponse,
// error事件捕獲
globalError: GlobalError
})
class App extends LennethApplication implements ILenneth {
$onMountingMiddlewares() {
this.use(logger());
}
}
- interceptor
其實(shí)也是一個(gè)中間件循帐,只不過(guò)在最前執(zhí)行
import { IMiddleware, Middleware, HeaderParams, Next, TNext } from "lenneth";
@Middleware()
export class Interceptor implements IMiddleware {
async use(@HeaderParams() header: any, @Next() next: TNext) {
console.log("Interceptor", header);
await next();
}
}
- response
中間件,在最后執(zhí)行舀武,默認(rèn)開啟拄养,可以覆蓋
import { IMiddleware, IContext, TResponse, TNext } from "@interfaces";
import { Middleware, Response, Next } from "@decorators";
import { HttpStatus, ResponseStatus } from "@common";
import { LennethError } from "./Lenneth-error";
@Middleware()
export class LennethResponse implements IMiddleware {
async use(
@Response() response: TResponse,
@Next() next: TNext,
ctx: IContext
) {
try {
// 執(zhí)行前面所有的中間件
await next();
// 統(tǒng)一處理返回
if (response.body) {
return (response.body = {
code: 0,
message: ResponseStatus.SUCCESS,
data: response.body
});
}
return (response.body = { code: 0, message: ResponseStatus.SUCCESS });
} catch (err) {
ctx.status = err.code;
response.status = HttpStatus.OK;
if (err instanceof LennethError) {
response.body = {
code: err.code,
message: err.message || ResponseStatus.ERROR
};
} else {
response.body = {
code: err.code || HttpStatus.INTERNAL_SERVER_ERROR,
message: err.message || ResponseStatus.ERROR
};
// 未識(shí)別錯(cuò)誤 拋至最外層error全局處理
throw err;
}
}
}
}
- controller
controller 主要是設(shè)置 router 和注入 services
router 的修飾器有 Post,Get 等,params 參數(shù)的獲取同 spring,注入 service 使用修飾器 Autowired,這個(gè)也和 spring 一致
import {
Controller,
Autowired,
Post,
Get,
RequestBody,
PathVariable,
Response,
TResponse,
UseBefore,
Description
} from "lenneth";
import { UserService } from "../services";
import { IUserInfo } from "../interface";
import { UserAuth, RuleAuth } from "../middleware";
@Controller("/user")
export class UserController {
@Autowired() userService: UserService;
@Post("/add")
@Description("添加會(huì)員")
@UseBefore(UserAuth, RuleAuth)
async addUser(
@RequestBody() user: IUserInfo,
@Response() response: TResponse
) {
response.body = this.userService.addUser(user);
}
@Get("/detail/:userId")
@UseBefore(UserAuth)
@Description("查詢會(huì)員")
async getUser(
@PathVariable("userId") userId: string,
@Response() response: TResponse
) {
response.body = this.userService.getUserInfo(userId);
}
}
- middleware
middleware 本質(zhì)上其實(shí)就是 koa 的中間件,只不過(guò)我在此基礎(chǔ)上又抽象出一層方法來(lái)引入獲取 params 的方法瘪匿,用來(lái)方便開發(fā)
在 controller 每個(gè) api 上跛梗,使用 UseBefore 修飾器即可使用這些 middleware,在運(yùn)行期棋弥,middleware 先于 controller 定義的接口核偿,如果 middleware 沒有調(diào)用 next 函數(shù),則不會(huì)調(diào)用下一個(gè)中間件(kao 洋蔥模型)
import { IMiddleware, Middleware, Next, TNext, HeaderParams } from "lenneth";
@Middleware()
export class UserAuth implements IMiddleware {
async use(@HeaderParams() headers: any, @Next() next: TNext) {
await next();
}
}
@Middleware()
export class RuleAuth implements IMiddleware {
async use(@HeaderParams() headers: any, @Next() next: TNext) {
await next();
}
}
- service
這個(gè)模塊只是做一個(gè)類輸出方法
export class UserService {
addUser(userInfo: IUserInfo) {
return userInfo;
}
getUserInfo(id: string) {
return {
name: "zhangsan",
age: 30
};
}
}
單元測(cè)試
yarn test
案例
項(xiàng)目地址
說(shuō)在最后
當(dāng)初做項(xiàng)目的時(shí)候顽染,在 github 上搜過(guò)一個(gè)項(xiàng)目漾岳,是基于 express 的--ts-express-decorators,里面有很多不錯(cuò)的設(shè)計(jì)粉寞,lenneth 里的服務(wù)啟動(dòng)生命周期就是照搬其中的尼荆。不過(guò)我不喜歡把 node 弄得那么大,那么全唧垦,而且捅儒,koa 本身就是一個(gè)極簡(jiǎn)的應(yīng)用,所以振亮,lenneth 僅僅只是做了一層封裝巧还,繁簡(jiǎn)自然。