Midway FaaS 提供了函数的本地开发、调用、测试整个链路,同时针对不同的平台触发器,也提供了完整的 TypeScript 参数定义。
开发函数
普通函数
普通的函数都是通过 “事件触发” - “函数调用” 的模型被执行,整个是一个过程式的执行逻辑。在 Midway FaaS 的封装后,我们将其变成了 class 写法,在便于 IoC 体系使用的同时,也满足代码复用。
class 的写法如下。
import { Func, Inject, Provide } from '@midwayjs/decorator'; // 装饰器包
import { FaaSContext, FunctionHandler } from '@midwayjs/faas'; // midway faas 框架包
@Provide() // 提供 IoC 容器扫描标识
@Func('index.handler') // 标注函数入口
export class IndexService implements FunctionHandler {
async handler(event: any) { // <---- 函数体
return 'hello world'; // 函数返回值
}
}
在上面我们的逻辑代码主要写在函数体中。
入口对应关系
Midway FaaS 通过 @Func
装饰器找到函数入口并执行, @Func
的入参为一个 handler 名字符串。这个字符串会和 f.yml
中的函数相匹配对应。
@Func('index.handler') // 函数的 handler
functions:
index:
handler: index.handler
index2:
handler: index.handler ## 这样配,两个不同的函数,会指向相同的代码
入口函数
@Func
装饰器既可以修饰 class,又可以修饰方法。
修饰到类
当修饰在 class 上时,默认会以 handler
方法作为函数入口。
@Provide()
@Func('index.handler')
export class IndexService implements FunctionHandler {
async handler(event: any) {
return 'hello world';
}
}
修饰到方法
**
而当 @Func
装饰器修饰到方法上时,更为自由,方法名可以完全自定义。
@Provide()
export class IndexService {
@Func('hi.handler')
async sayHi(event: any) {
return 'hello world';
}
@Func('hello.handler')
async sayHello(event: any) {
return 'hello world';
}
}
通过这样的方式,可以把多个函数放在同一个 class 上,中间的属性可以进行复用。
@Provide()
export class IndexService {
@Inject()
ctx: FaaSContext; // 代码层面复用了这个 ctx 属性,但是实际调用时,上下文是分开的。
@Func('hi.handler')
async sayHi(event: any) {
// TODO
}
@Func('hello.handler')
async sayHello(event: any) {
// TODO
}
}
函数参数
针对各个平台的函数入参,我们尽可能的做了 TypeScript 定义。
import { FaaSContext, FC, SCF } from '@midwayjs/faas';
import { Func, Provide } from '@midwayjs/decorator';
@Provide()
export class TestTrigger {
@Func('oss.handler')
async aliyunOSS(event: FC.OSSEvent) {
// TODO
}
@Func('cos.handler')
async tecentCOS(event: SCF.COSEvent) {
// TODO
}
}
具体的 Event 类型定义,请参考我们各个平台的触发器文档,比如 OSS 触发器。
HTTP 类型的触发器
包括阿里云的 http,以及其他平台的 API 网关类型,这些触发器最终是为了产生一个 HTTP 接口,我们希望在写 HTTP 接口中能够和传统应用尽可能接近,易于理解,对其入参进行了封装。
比如可以直接通过 ctx.query
获取 query 参数,通过 ctx.request.body
获取到 POST 的 body 参数,返回的时候可以直接挂载到 ctx.body
上。
import { FaaSContext, FC } from '@midwayjs/faas';
import { Func, Inject, Provide } from '@midwayjs/decorator';
@Provide()
export class HTTPTrigger {
@Inject()
ctx: FaaSContext; // context
@Func('every.handler')
async getMethod() {
const query = this.ctx.query;
const body = this.ctx.request.body;
this.ctx.body = 'hello world';
}
}
更多的 FaaSContext 函数上下文 API ,请查询 函数上下文文档。
本地调用
普通触发器函数
使用 f invoke
命令来调用普通函数,我们针对大多数平台的触发器都提供了模拟数据。
比如一个普通函数。
functions:
index: # <----- 这个才是函数名
handler: index.handler
events:
- timer:
type: every
value: 1m
在执行以下命令后,会自动调用到函数的 handler。
$ f invoke -f [函数名]
HTTP、API 网关触发函数
针对 HTTP 和 API 网关类型的函数开发,我们做了一个高级支持,通过启动一个真实的 HTTP 服务器来模拟传统 Web 栈开发的样子。
比如一个 HTTP 函数:
functions:
index:
handler: index.handler
events:
- http:
path: /*
method: get
执行 f invoke -p
,默认会以 3000 端口启动服务。
如果函数代码如下:
import { Func, Provide } from '@midwayjs/decorator';
@Provide()
export class IndexService {
@Func('index.handler')
async handler() {
return 'hello world';
}
}
则你能在浏览器中看到下面的输出。
:::info
第一次需要完整编译,会慢一些。
:::
整个启动服务的命令为。
$ f invoke -p
$ f invoke -p 3000 // 默认端口 3000,可以修改
$ f invoke -p 3000 --debug // 调试模式启动服务