lenneth -- 基于koa2的極簡(jiǎn)框架

說(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

案例

lenneth-demo

項(xiàng)目地址

lenneth

說(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)自然。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末双炕,一起剝皮案震驚了整個(gè)濱河市狞悲,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌妇斤,老刑警劉巖摇锋,帶你破解...
    沈念sama閱讀 219,589評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異站超,居然都是意外死亡荸恕,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門死相,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)融求,“玉大人,你說(shuō)我怎么就攤上這事算撮∩穑” “怎么了?”我有些...
    開封第一講書人閱讀 165,933評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵肮柜,是天一觀的道長(zhǎng)陷舅。 經(jīng)常有香客問(wèn)我,道長(zhǎng)审洞,這世上最難降的妖魔是什么莱睁? 我笑而不...
    開封第一講書人閱讀 58,976評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上仰剿,老公的妹妹穿的比我還像新娘创淡。我一直安慰自己,他們只是感情好南吮,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,999評(píng)論 6 393
  • 文/花漫 我一把揭開白布琳彩。 她就那樣靜靜地躺著,像睡著了一般旨袒。 火紅的嫁衣襯著肌膚如雪汁针。 梳的紋絲不亂的頭發(fā)上术辐,一...
    開封第一講書人閱讀 51,775評(píng)論 1 307
  • 那天砚尽,我揣著相機(jī)與錄音,去河邊找鬼辉词。 笑死必孤,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的瑞躺。 我是一名探鬼主播敷搪,決...
    沈念sama閱讀 40,474評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼幢哨!你這毒婦竟也來(lái)了赡勘?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,359評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤捞镰,失蹤者是張志新(化名)和其女友劉穎闸与,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體岸售,經(jīng)...
    沈念sama閱讀 45,854評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡践樱,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,007評(píng)論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了凸丸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拷邢。...
    茶點(diǎn)故事閱讀 40,146評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖屎慢,靈堂內(nèi)的尸體忽然破棺而出瞭稼,到底是詐尸還是另有隱情,我是刑警寧澤腻惠,帶...
    沈念sama閱讀 35,826評(píng)論 5 346
  • 正文 年R本政府宣布环肘,位于F島的核電站,受9級(jí)特大地震影響妖枚,放射性物質(zhì)發(fā)生泄漏廷臼。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,484評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望荠商。 院中可真熱鬧寂恬,春花似錦、人聲如沸莱没。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,029評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)饰躲。三九已至牙咏,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間嘹裂,已是汗流浹背妄壶。 一陣腳步聲響...
    開封第一講書人閱讀 33,153評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留寄狼,地道東北人丁寄。 一個(gè)月前我還...
    沈念sama閱讀 48,420評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像泊愧,于是被迫代替她去往敵國(guó)和親伊磺。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,107評(píng)論 2 356

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理删咱,服務(wù)發(fā)現(xiàn)屑埋,斷路器,智...
    卡卡羅2017閱讀 134,672評(píng)論 18 139
  • 原文鏈接:http://www.reibang.com/p/6b816c609669 前傳 出于興趣最近開始研究k...
    懸筆e絕閱讀 7,218評(píng)論 1 11
  • 前言:我一個(gè)有金融工作經(jīng)驗(yàn)12年的工作者,我也想成為網(wǎng)紅即寡,不是我想成名徊哑,而是想通過(guò)網(wǎng)紅襯托出自身價(jià)值,證明自...
    陳東紅先生閱讀 516評(píng)論 0 2
  • Given an array with n integers, you need to find if there...
    matrxyz閱讀 908評(píng)論 0 0
  • 端午放假三天看到同學(xué)們都為自己的爸媽大顯身手聪富,做了各種美食莺丑,兒子看了終于按耐不住了,說(shuō)實(shí)話我沒有開口叫他做墩蔓,我就想...
    李宇航媽媽閱讀 117評(píng)論 0 0