App Engine 之 Action 中的装饰器
Action是指可以在Flow, Process, Business Process等对象中调用的Script。
Action需要满足如下条件:
有明确的输入输出参数
有唯一的入口方法
下面是一个是一个计算器Action的示例代码:
/* * Copyright (c) Huawei Technologies Co., Ltd. 2012-2019. All rights reserved. * * The more action information please see: * https://dev.besclouds.com/u-route/baas/doc/blog/#/zh-cn/script/action * */ import { Decimal } from 'decimal'; import { Error } from 'error'; export class Input { @action.param({ type: "String", required: true, description: "the operation type" }) op: string; @action.param({ type: "Number", required: true, description: "the operation value 1" }) value1: Decimal; @action.param({ type: "Number", required: true, description: "the operation value 2" }) value2: Decimal; } export class Output { @action.param({ type: "Number", required: true }) result: Decimal; } export class Calculator { @action.method({ input: "Input", output: "Output", description: "do a operation" }) run(input: Input): Output { let output = new Output(); switch (input.op) { case "+": output.result = this.sum(input.value1, input.value2); break; case "-": output.result = this.sub(input.value1, input.value2); break; default: throw new Error("00001", `unsupported calculator operator ${input.op}`); } return output; } sum(a: Decimal, b: Decimal): Decimal { return a.Add(b) } sub(a: Decimal, b: Decimal): Decimal { return a.Sub(b) } }
Action可以通过restful请求调用,发送如下请求参数进行求和计算:
{ "op": "+", "value1": 99, "value2": 100 }
看到这里,小伙伴可能对上面的代码中的几个装饰器有些疑问。(装饰器是Typescript为类声明及类成员添加标注的一种方式,了解更多可以到这里:https://www.w3cschool.cn/typescript/typescript-decorators.html )
从App Engine官方文档来看,代码中几个装饰器的具体作用如下:
@action.param:注解参数。所有的输入或输入参数必须封装在一个class中,作为实例成员,每一个参数均被action.param注解。
@action.method:标识Action的入口方法。被标识的方法必须是class的实例方法,且在一个Action文件中,有且只能有一个入口方法。
看到这里,连小编也是一头雾水,什么是入口方法?为什么加个method 装饰器就能标识一个入口方法?为什么要用param 装饰器注解参数呢?啥叫注解?这是什么原理呢?于是小编就寻找这些装饰器的定义,看看它们到底是怎么实现的。
结果发现,这些装饰器函数只有空空的声明,根本没有实现代码,它们看起来不执行任何动作:
interface Action { /** * To mark the class as an action parameter class or method class. * @returns {Function|*} */ object(options: ObjectOptions): Function; /** * To mark the entry point method of the action * @returns {Function|*} */ method(options: MethodOptions): Function; /** * To mark action's parameter option * @returns {Function|*} */ param(options: ParamOptions): Function; }
Typescript本身是不会定义入口方法的。所谓的入口方法即类似于C语言的main()函数,Typescript 没有main()函数,它是按代码行自上至下的顺序依次执行的。我们可以看到,上面的Action 代码只定义了类,并没有在代码中实例化类,所以把上面的代码用标准的Typescript编译器编译运行后,不会执行任何动作。
那么这里的method装饰器是怎样把run()函数定义为该Action的入口方法呢?在咨询了我们的开发大牛老邓同学之后,小编总算明白了,原来App Engine的编译器做了手脚。
App Engine 在编译上述代码时,通过分析语法树,将 @action.method 装饰的方法所在的类实例化,并在运行时通过实例调用了被装饰的run()方法,原来这一切动作都在我们看不见的地方发生了!
那么@action.param 所谓的“注解” 是什么作用呢?我们来看一下这段代码:
@action.param({ type: "String", required: true, description: "the operation type" }) op: string;
代码通过Action.param 告诉编译器,被装饰的成员是一个String类型,且作为入参,必须携带该参数。AppEngine在运行代码时,如果发现该入参成员为空,或者该入参成员不是String类型,那么会报错。反之,如果这里不加@action.param装饰,那么AppEngine运行时就不会强制检查入参空值及类型。
这下你明白了吗?
再来看另外一个我们会经常遇到的装饰器:
@useObject(['foo'])
在脚本中,引用对象前总能看到useObject装饰器。这个装饰器的作用是告诉编译器,这段代码引用了一个叫作 foo 的对象,打包编译时需要将 foo 打包进来不要丢了。小编咨询了老邓,这个装饰器已经是历史遗留了,很早之前的版本没有APP的概念,对象与代码没有像当前版本那样都关联在APP下,所以就要通过 useObject 装饰器告诉编译器需要打包哪些对象。为了保持兼容性,当前版本仍保留了useObject 装饰器。你可能会发现,当前版本在有些情况下编译器仍会提示“使用对象前请先使用useObject”,所以在脚本中引用对象前,小编还是建议用useObject 装饰器。在未来版本,该装饰器可能会真正退出历史舞台。
- 点赞
- 收藏
- 关注作者
评论(0)