云侧工程云函数开发
云函数是一项Serverless计算服务,提供FaaS(Function as a Service)能力,可以类比于服务器端对外提供的API接口,相比于服务器端优势在于开发者只需要聚焦于业务逻辑实现、开发并上传函数代码,通过少量的代码可以实现类似服务器端操作数据库、存储等能力。还可以根据函数的实际使用流量对函数进行弹性伸缩,而无需对服务器资源进行管理,替开发者完全解决了传统应用开发与运维中的诸多复杂事务(如服务器配置与管理、代码部署、负载均衡、弹性伸缩、高可用保证等等)。
DevEco Studio集成开发工具提供端云协同开发能力,在云侧工程创建函数、实现业务逻辑代码、调试函数、部署函数,端侧通过少量的代码调用部署到AGC云端的云函数获取业务数据。云侧工程开发云函数总体流程如下所示:
- 创建并配置函数:云侧工程中创建函数、为函数配置入口以及调用的触发器等。
- 开发函数:函数创建并配置完成后,在函数入口文件中编写业务逻辑代码。
- 调试函数:业务逻辑代码编写完成后,在DevEco Studio中调试函数代码是否运行正常。
- 部署函数:完成函数代码开发与调试后,开发者可以将函数部署到AGC云端,支持单个部署和批量部署。
1、创建配置云函数
1)创建云函数
展开云侧工程(CloudProgram),右击cloudfunctions
目录,选择New > Cloud Function
打开创建云函数向导。
在创建云函数向导中输入云函数名称(如calculate-baby-age,该函数用于计算宝宝年龄,包含年、月、日、天数),函数名称长度2-63个字符,仅支持小写英文字母、数字、中划线(-),首字符必须为小写字母,结尾不能为中划线(-)。然后在“Select the Cloud Function Type”栏选择云函数Cloud Function(云对象Cloud Object会在后面的项目中进行介绍),最后点击OK按钮,等待DevEco Studio完成云函数创建。
DevEco Studio完成云函数创建后,会在cloudfunctions
目录新建get-due-date
函数目录,并创建函数配置文件function-config.json
、函数入口文件myCloudFunction.ts
以及依赖配置文件package.json
。
2)配置云函数
在云函数配置文件中,我们只需要关注配置触发器属性triggers
,通过触发器暴露的触发条件来实现函数调用。而函数入口属性handler
和函数类型functionType
默认即可,特别需要注意的是functionType
的值在创建时根据开发者选择的“Select the Cloud Function Type”类型自动生成,“0”表示云函数,“1”表示云对象,不可手动修改,否则会导致云函数部署失败。
DevEco Studio云侧工程云函数当前仅支持HTTP触发器,云函数创建成功后,会在云函数配置文件function-config.json
文件中自动完成HTTP触发器配置。函数部署到云端后会自动生成触发器URL,开发者向URL发起HTTP请求时触发函数。
{"handler": "calculateBabyAge.myHandler","functionType": 0,"triggers": [{"type": "http","properties": {"enableUrlDecode": true,"authFlag": "true","authAlgor": "HDA-SYSTEM","authType": "apigw-client"}}]
}
type
:触发器类型,端云协同工具中仅支持配置为http
。properties
:触发器属性。enableUrlDecode
:通过HTTP触发器触发函数时,对于contentType
为application/x-www-form-urlencoded
的触发器请求,是否使用URLDecoder对请求body
进行解码再转发到函数中。true
表示启用;false
表示不启用。authFlag
:是否鉴权,默认为true
。authAlgor
:鉴权算法,默认为HDA-SYSTEM
。authType
:HTTP触发器的认证类型。apigw-client
:端侧网关认证,适用于来自APP客户端侧(即本地应用或者项目)的函数调用。cloudgw-client
:云侧网关认证,适用于来自APP服务器侧(即云函数)的函数调用。
2、开发云函数
云函数创建和配置完成后,接下来可以编写函数业务逻辑代码。打开函数入口文件getDueDate.ts
,在入口方法myHandler
中编写业务逻辑代码,不建议重命名方法名称。
let myHandler = async function (event, context, callback, logger) {logger.info(event);// do something herecallback({code: 0,desc: "Success."});
};export { myHandler };
myHandler
:入口方法名称。event
:调用方法传递的事件对象,JSON格式。采用触发器的方式实现函数调用,函数入口方法需要遵循对应触发器的event
对象字段要求。本项目使用HTTP触发器,event
对象数据格式如下所示:path
:HTTP请求触发URL路径。httpMethod
:触发器请求方式,目前HTTP触发器仅支持POST事件请求方式。headers
:请求头,指明请求或描述消息,一般包含"authorization"、“content-length”、“x-tenant-id”、“x-business-id”、“x-product-id”、“content-type”、“connection”、"accept-encoding"等字段,开发者可自定义。queryStringParameters
:请求数据的消息体,可由您自定义,JSON格式的字符串,最大为4MB。如果函数由应用客户端调用,在函数定义中需要在event.body
获取用户客户端传入的HTTP body体,再从body体中解析传入的参数值。isBase64Encoded
:消息体是否为base64编码的布尔值标识。true:消息体为base64编码;false:消息体非base64编码。
context
:函数运行时上下文对象,封装了日志接口、回调接口、环境变量env
对象等。callback
:事件处理结果。函数必须通过显式调用callback(object)
将事件处理结果返回给AGC,结果可以是任意对象,但必须与JSON.stringify
兼容,AGC会将结果转换成JSON字符串,返回给调用方。callback
执行完成,即函数执行结束。logger
:代码中可以使用logger
接口记录日志,目前支持四种级别:logger.debug()
logger.error()
logger.warn()
logger.info()
let myHandler = async function (event, context, callback, logger) {logger.info(event);// 获取端侧传递的参数let birthday = event.body?JSON.parse(event.body).birthday : event.birthday;try {// 计算宝宝的年龄const babyAge = calculateBabyAge(birthday);// 返回结果callback({code: 200,desc: "Success.",data: babyAge})} catch (error) {// 返回结果callback({code: 0,desc: error});}
};export { myHandler };/*** 表示宝宝年龄的类*/
class BabyAge {years: number;months: number;days: number;totalDays: number;/*** @param {number} years - 年数* @param {number} months - 月数* @param {number} days - 天数* @param {number} totalDays - 总天数*/constructor(years, months, days, totalDays) {this.years = years;this.months = months;this.days = days;this.totalDays = totalDays;}/*** 获取格式化的年龄字符串* @returns {string} 格式化的年龄字符串*/toString() {return `${this.years}岁${this.months}个月${this.days}天`;}/*** 获取总天数的描述* @returns {string} 总天数的描述*/getTotalDaysString() {return `总共${this.totalDays}天`;}/*** 获取完整的年龄描述* @returns {string} 完整的年龄描述*/getFullDescription() {return `${this.toString()}(${this.getTotalDaysString()})`;}/*** 转换为JSON对象* @returns {Object} 包含年龄信息的对象*/toJSON() {return {years: this.years,months: this.months,days: this.days,totalDays: this.totalDays};}
}/*** 计算宝宝从出生到现在的详细年龄信息* @param {string} birthDate - 宝宝出生日期,格式:YYYY-MM-DD* @returns {BabyAge} 包含年、月、天的BabyAge实例*/
function calculateBabyAge(birthDate) {// 将生日字符串转换为Date对象const birth = new Date(birthDate);const today = new Date();// 确保输入的日期格式正确if (isNaN(birth.getTime())) {throw new Error('请输入正确的日期格式:YYYY-MM-DD');}// 检查生日是否在将来if (birth > today) {throw new Error('出生日期不能在将来');}// 计算年份差异let years = today.getFullYear() - birth.getFullYear();let months = today.getMonth() - birth.getMonth();let days = today.getDate() - birth.getDate();// 处理月份为负数的情况if (days < 0) {months--;// 获取上个月的最后一天const lastMonth = new Date(today.getFullYear(), today.getMonth(), 0);days += lastMonth.getDate();}// 处理月份为负数的情况if (months < 0) {years--;months += 12;}// 计算总天数const totalDays = Math.floor((today.getTime() - birth.getTime()) / (1000 * 60 * 60 * 24));// 返回BabyAge实例return new BabyAge(years, months, days, totalDays);
}