CNode

气球:一个基于express和typescript的nodejs框架

分享
Vvincent178发布于9 年前最后回复8 年前31 回复15428 浏览0 收藏

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

功能

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

快速开始

  • 克隆 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 项目地址

查看回复

回复 (31)

Y
YAOHAO9#1·9 年前

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

B
bettermu#2·9 年前

感谢分享

R
richenlin#3·9 年前

不考虑进化到koa么

Y
yangchongduo#4·9 年前

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

V
vincent178#5·9 年前
引用 YAOHAO9感觉挺新颖的,感觉像是在做java。直接上公司项目有什么建议么?

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

V
vincent178#6·9 年前

@richenlin 没有看到koa的优势。

Y
yuedun#7·9 年前

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

V
vincent178#8·9 年前

@YAOHAO9 1.4.5版本修复了

吹气球,吹个大气球。。

Y
YAOHAO9#10·9 年前

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

V
vincent178#11·9 年前

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

Y
YAOHAO9#12·9 年前
引用 vincent178可以,建议放在 src/initializers/ 文件夹

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

Y
yahue#13·9 年前

666

V
vincent178#14·9 年前

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

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

Y
YAOHAO9#15·9 年前
引用 vincent178@YAOHAO9 在ApplicationSettings里面配置了rootDir之后,这个rootDir的export出来的都会被自动加载。 源代码在loon/src/server/Appl...

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

V
vincent178#16·9 年前

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

D
dadaa1#17·9 年前

mark

L
LowLoop#18·9 年前

Mark

H
heightzhang#19·9 年前

D
dengnan123#20·9 年前

Mark

Q
qinyang1980#21·9 年前

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

C
chuangtim#22·9 年前

不错,spring IOC和AOP很强大,现在写nodejs和java一样,赞👍

I
i5ting#23·9 年前

挺好的,加油

X
xfstart07#24·9 年前

加油加油

M
MinionsDave#25·9 年前

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

H
hyj1991#26·9 年前

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

V
vincent178#28·9 年前
引用 xfstart07加油加油

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

V
vincent178#29·9 年前

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

W
wangcj10086#30·8 年前

Mark 学习下

Z
zuohuadong#31·8 年前
参与回复
登录后即可参与回复。登录