Midway FaaS 提供了函数的本地开发、调用、测试整个链路,同时针对不同的平台触发器,也提供了完整的 TypeScript 参数定义。

开发函数

普通函数

普通的函数都是通过 “事件触发” - “函数调用” 的模型被执行,整个是一个过程式的执行逻辑。在 Midway FaaS 的封装后,我们将其变成了 class 写法,在便于 IoC 体系使用的同时,也满足代码复用。

class 的写法如下。

  1. import { Func, Inject, Provide } from '@midwayjs/decorator'; // 装饰器包
  2. import { FaaSContext, FunctionHandler } from '@midwayjs/faas'; // midway faas 框架包
  3. @Provide() // 提供 IoC 容器扫描标识
  4. @Func('index.handler') // 标注函数入口
  5. export class IndexService implements FunctionHandler {
  6. async handler(event: any) { // <---- 函数体
  7. return 'hello world'; // 函数返回值
  8. }
  9. }

在上面我们的逻辑代码主要写在函数体中。

入口对应关系

Midway FaaS 通过 @Func 装饰器找到函数入口并执行, @Func 的入参为一个 handler 名字符串。这个字符串会和 f.yml 中的函数相匹配对应。

  1. @Func('index.handler') // 函数的 handler
  1. functions:
  2. index:
  3. handler: index.handler
  4. index2:
  5. handler: index.handler ## 这样配,两个不同的函数,会指向相同的代码

入口函数

@Func 装饰器既可以修饰 class,又可以修饰方法。

修饰到类

当修饰在 class 上时,默认会以 handler 方法作为函数入口。

  1. @Provide()
  2. @Func('index.handler')
  3. export class IndexService implements FunctionHandler {
  4. async handler(event: any) {
  5. return 'hello world';
  6. }
  7. }

修饰到方法
**
而当 @Func 装饰器修饰到方法上时,更为自由,方法名可以完全自定义。

  1. @Provide()
  2. export class IndexService {
  3. @Func('hi.handler')
  4. async sayHi(event: any) {
  5. return 'hello world';
  6. }
  7. @Func('hello.handler')
  8. async sayHello(event: any) {
  9. return 'hello world';
  10. }
  11. }

通过这样的方式,可以把多个函数放在同一个 class 上,中间的属性可以进行复用。

  1. @Provide()
  2. export class IndexService {
  3. @Inject()
  4. ctx: FaaSContext; // 代码层面复用了这个 ctx 属性,但是实际调用时,上下文是分开的。
  5. @Func('hi.handler')
  6. async sayHi(event: any) {
  7. // TODO
  8. }
  9. @Func('hello.handler')
  10. async sayHello(event: any) {
  11. // TODO
  12. }
  13. }

函数参数

针对各个平台的函数入参,我们尽可能的做了 TypeScript 定义。

  1. import { FaaSContext, FC, SCF } from '@midwayjs/faas';
  2. import { Func, Provide } from '@midwayjs/decorator';
  3. @Provide()
  4. export class TestTrigger {
  5. @Func('oss.handler')
  6. async aliyunOSS(event: FC.OSSEvent) {
  7. // TODO
  8. }
  9. @Func('cos.handler')
  10. async tecentCOS(event: SCF.COSEvent) {
  11. // TODO
  12. }
  13. }

具体的 Event 类型定义,请参考我们各个平台的触发器文档,比如 OSS 触发器

HTTP 类型的触发器

包括阿里云的 http,以及其他平台的 API 网关类型,这些触发器最终是为了产生一个 HTTP 接口,我们希望在写 HTTP 接口中能够和传统应用尽可能接近,易于理解,对其入参进行了封装。

比如可以直接通过 ctx.query 获取 query 参数,通过 ctx.request.body 获取到 POST 的 body 参数,返回的时候可以直接挂载到 ctx.body 上。

  1. import { FaaSContext, FC } from '@midwayjs/faas';
  2. import { Func, Inject, Provide } from '@midwayjs/decorator';
  3. @Provide()
  4. export class HTTPTrigger {
  5. @Inject()
  6. ctx: FaaSContext; // context
  7. @Func('every.handler')
  8. async getMethod() {
  9. const query = this.ctx.query;
  10. const body = this.ctx.request.body;
  11. this.ctx.body = 'hello world';
  12. }
  13. }

更多的 FaaSContext 函数上下文 API ,请查询 函数上下文文档

本地调用

普通触发器函数

使用 f invoke 命令来调用普通函数,我们针对大多数平台的触发器都提供了模拟数据。

比如一个普通函数。

  1. functions:
  2. index: # <----- 这个才是函数名
  3. handler: index.handler
  4. events:
  5. - timer:
  6. type: every
  7. value: 1m

在执行以下命令后,会自动调用到函数的 handler。

  1. $ f invoke -f [函数名]

HTTP、API 网关触发函数

针对 HTTP 和 API 网关类型的函数开发,我们做了一个高级支持,通过启动一个真实的 HTTP 服务器来模拟传统 Web 栈开发的样子。

比如一个 HTTP 函数:

  1. functions:
  2. index:
  3. handler: index.handler
  4. events:
  5. - http:
  6. path: /*
  7. method: get

执行 f invoke -p ,默认会以 3000 端口启动服务。

如果函数代码如下:

  1. import { Func, Provide } from '@midwayjs/decorator';
  2. @Provide()
  3. export class IndexService {
  4. @Func('index.handler')
  5. async handler() {
  6. return 'hello world';
  7. }
  8. }

则你能在浏览器中看到下面的输出。
image.png :::info 第一次需要完整编译,会慢一些。 :::

整个启动服务的命令为。

  1. $ f invoke -p
  2. $ f invoke -p 3000 // 默认端口 3000,可以修改
  3. $ f invoke -p 3000 --debug // 调试模式启动服务

欢迎关注 ➡️ image.png