气球:一个基于express和typescript的nodejs框架
发布于 5 个月前 作者 vincent178 3625 次浏览 来自 分享

一个类似于springmvc的nodejs框架,内置了常用的express包,有丰富的可选配置,依赖注入. 项目地址 文档

功能

  • 依赖注入
  • 服务类
  • Result 路由和控制器
  • 中间件
  • 过滤器
  • 模型
  • 转化器
  • 启动器

快速开始

<br>

  • 克隆 loon-boilerplate 项目
$ git clone https://github.com/loon-project/loon-boilerplate
$ cd loon-boilerplate
$ npm install
  • 开始编译typescript并启动服务器
$ npm run dev

控制器

@RestController('/admin')
class AdminController {

  @Get('/users/:id')
  public userAction(@PathParam('id') id: number) {
      # incoming request is /admin/users/1
      # then id inside this action would be 1
  }

  @Get('/users')
  public listUsersAction(@QueryParam('is_active') isActive: boolean) {
      # incoming request is /admin/users?is_active=true
      # then isActive inside this action would be true

      isAction === true # => true
  }

  @Post('/users')
  public createUserAction(@BodyParam('user') user: any) {
      # incoming request is POST /admin/users with json body
      # {
      #   user: {name: "Jack"}
      # }
      # then the user inside this action would be a object

      user.name === "Jack" # => true
  }
}

过滤器

AuthenticateFilter 前置过滤器检查是否用户已经登陆, 如果用户还没有登陆, 就直接返回 403 状态码。

使用时 AuthenticatedResourceController 加上一个 @BeforeFilter(AuthenticateFilter) 的注解, 这就意味着所有这个 Controller 里面的 路由都会使用前置过滤器 AuthenticateFilter。 如果这个 Controller 需要几个能让匿名用户访问的路由, 可以在使用注解时加上参数来达到部分使用过滤器的目的。

@Filter()
class AuthenticateFilter implements IMiddleware {

  public use(@Res() res: Express.Response, @Next() next: Express.NextFunction) {
    if (// use is logged in) {
      next();
    } else {
      res.sendStatus(403);
    }
  }
}

@RestController()
@BeforeFilter(AuthenticateFilter)
class AuthenticatedResourceController {}

中间件

注解了 @Middleware 并且实现了 IMiddleware 的类自动会识别并加载为 Middleware

@Middleware({order: 0})
class ELKMiddleware implements IMiddleware {

  public use(@Next() next: Express.NextFunction) {
    // start tracking
    next()
    // end tracking
    // send to elk
  }
}

错误中间件

@ErrorMiddleware 用法和 @Middleware 基本一样, 有一个不同点是你能通过 @Err 拿到错误 Object。

@ErrorMiddleware()
class ExceptionHandler implements IMiddleware {

  public use(@Err() err: any, @Res() res: Express.Response) {
    console.log(err.message);
    res.sendStatus(400);
  }
}

模型

模型通过添加 @Property 注解增加了转化的能力。


class User {

  @Property()
  firstName: string;

  @Property()
  lastName: string;
}

@RestController("/users")
class UserController {

  @Post("")
  public createAction(@BodyParam('user') user: User) {
    user instanceof User # => true
  }
}

控制器中的参数 user 制定了类是 User, 当带有 http body 的请求到来时:

{
  user: {
    firstName: "Jack",
    lastName: "Hai"
  }
}

user 这个变量会自动的转化成 User model 的实例。

自定义转化器

属性也支持传一个实现了 IConverter 借口的类作为自定义转化器。

@Service()
export class MomentConverter implements IConverter {

    public deserialize(data: any, klassProperty: string, objectProperty: string): any {
        const value = data[objectProperty];

        if (typeof value === 'undefined') {
            return;
        }

        const m = Moment.utc(value);

        if (m.isValid()) {
            return m;
        }

        return;
    }
}

class User {

  @Property({converter: MomentConverter})
  public registerDate: Moment.Moment;
}

这个例子显示了如何写一个转化 string/Date 对象到 moment utc 对象。

服务


@Service()
class UserService {
  // any logic
}

@RestController('/users')
class UserController {
  @Inject()
  private userService: UserService;

  @Get('')
  public indexAction() {
    this.userService.listUsers();
  }
}

在例子中, UserService 注解了 @Service, 然后被注入在了 UserController

文档更多详细的说明

欢迎star,fork还有各种issues 项目地址

28 回复

挺好的,加油

感觉特别赞,有空试用下。加油!

赞一个,话说用 ts 写项目可以不编译嘛,我也想学下 ts,但是总感觉每次写完和前端代码一样要打包的话好麻烦…

@xfstart07 原来你也看cnode啊,谢谢🙏

文档的静态文件使用了unpkg cdn加速,理论上打开速度会快很多。

感觉挺新颖的,感觉像是在做java。直接上公司项目有什么建议么?

不考虑进化到koa么

@YAOHAO9 上公司项目 🐂B 都开始用ts写node项目,厉害了

@YAOHAO9 问题不大,可以作为express的ts强化版使用,我们自己组已经小范围在用了。

@richenlin 没有看到koa的优势。

你这装饰器有点使用过度了,会让人难以理解

吹气球,吹个大气球。。

@yangchongduo 这个坑我踩定了,@vincent178 我push了一个分支到loon-boilerplate,不知道你有没有看到,没改什么东西,主要是配置。 @Initialize() class ServiceInitializer implements IInitializer { @Inject() private application: ApplicationLoader; public init() {
} } 这个我放在Application前面可以用,有固定的文件夹位置么,在启动Application前去加载。但是我觉得这个跟$beforeInit、 $afterInit这些有点重复。希望可以给我点建议。

可以,建议放在 src/initializers/ 文件夹

@vincent178 没有用,这个只是命名上的建议吧,我的意思是怎么像controller一样自动被注册到controllerRegister中。还有我看了源码也没有发现那里有去加载controllers文件夹下的controller。好神奇。。。 所以我的问题是 1:那些controller、service等是什么时候、怎样被注册的?是否Initialize也能自动被注册?我放在src/initializers/ 文件夹发现Initialize的init函数并没有被调用。 2:还有一个问题是component是什么东西,后端也有组件化开发了?文档好像中没有提到。

@YAOHAO9 在ApplicationSettings里面配置了rootDir之后,这个rootDir的export出来的都会被自动加载。 源代码在loon/src/server/ApplicationLoader.ts这个文件里面。 加载后会根据不同的类型分别挂载到express里面。

Component和Service这两个互为别名,是一个可以被依赖注入的基本组件。

@vincent178 发现一个小bug。image.png 应该loadComponents在init前面。不然Initializer永远没办法使用

@YAOHAO9 1.4.5版本修复了

@YAOHAO9 cnode没有邮件通知,你发github issue我会回的更及时一点。

是挺不错的。但是我已经用nest框架了。更全面些。

回到顶部