HarmonyOSNext 端云一体化(6)

HarmonyOSNext 端云一体化(6)

之前的文章中把云数据库、云存储都讲过了,这一章节要讲解的是云函数

云函数介绍

云函数其实就是 serverless 技术。可以理解云函数就实现传统后端中的具体业务,而无需关心服务器购买、部署、安全、性能等一系

列相关问题,专注于具体的业务开发。HarmonyOS Next 的云函数采用的是 typescript 的语法,这对熟悉 js 或者熟悉 ArkTs 的同学来说

都很容易上手。

另外云函数具有调用其他云函数、调用第三方接口、调用云存储、调用云数据库的能力。对于段云一体化开发的应用来说,可以根据这样的场景来使用云函数。

  1. 简单的数据库查询、云端文件的管理可以直接使用客户端操作的方式。
  2. 繁琐或者涉及安全的操作,可以把业务抽离到云函数端,这样更加容易管理项目。

本章节也会讲解如何云函数的开发、创建、调试、部署,以及在云函数端调用其他云函数、调用第三方接口、调用云存储、调用云

数据库。

创建云函数

可以选择创建云函数或者云对象。这里建议选择云对象更加方便业务逻辑的实现。为什么呢,举个例子。书籍的 crud 刚好放在一个对象中,增、删、改、查都可以做个一个对象的属性存在,更加容易方便管理。比如以下示例。

  • 云函数一览

    typescript 复制代码
    // 云函数
    let myHandler = async function (event, context, callback, logger) {
      //   这里写不同的业务逻辑,都是都耦合在一个myHandler中
      callback();
    };
    
    export { myHandler };
  • 云对象一览

    typescript 复制代码
    export class Book {
      //   增加
      add() {
        return {};
      }
    
      //   删除
      delete() {}
    
      //   修改
      update() {}
    
      //   查询
      query() {}
    }

新建云对象,云对象名称为 book。

得到以下新文件,其中 book.ts 是编写云对象 book 具体业务逻辑的。function-config.json是关于该云对象的相关配置如是否鉴权等。package.json是该云对象的描述文件,具体作用类似 oh-package.json,后续安装第三方依赖也会在这里登记信息。

function-config.json

文件内容如下。

json 复制代码
{
  "handler": "book.Book",
  "functionType": 1,
  "triggers": [
    {
      "type": "http",
      "properties": {
        "enableUrlDecode": true,
        "authFlag": "true",
        "authAlgor": "HDA-SYSTEM",
        "authType": "apigw-client"
      }
    }
  ]
}

相关解析如下

  • handler 表示云对象的入口

  • functionType 表示函数类型,"0"表示云函数,"1"表示云对象。"functionType"的值为创建时自动生成,不可手动修改,否则将导致云函数部署失败。

  • triggers 表示触发云对象的函数

    • 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 服务器侧(即云函数)的函数调用。

oh-package.json

文件内容如下。

json 复制代码
{
  "name": "book",
  "version": "1.0.0",
  "description": "",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {}
}

相关解释如下。

  • name:表示项目的名称
  • version:项目的版本号
  • description:项目的描述
  • scripts:一个包含各种脚本命令的对象
  • author:项目作者信息
  • license :项目所使用的开源许可证,这里是 "ISC"
  • dependencies:项目的依赖信息

book.ts

book.ts 的文件解析如下。

  1. methods1表示云对象具体的方法,可以有多个。
  2. params1params2是传递给该云对象的参数
  3. return 的内容是返回给云对象的调用者

调试云函数/对象

在开发云对象时,经常需要编写繁琐的业务逻辑,那么就必不可少对象的调试。DevEco Studio 内置了云对象的调试面板。按照以下步骤操作就可以方便对云对象进行调试了。

  1. 启动云对象调试,鼠标右键你要调试的云对象、选择 Debug 'book'

  2. 打开云对象调试面板

  3. 云对象日志面板

    当我们在云对象中使用 console.log 调试代码时,日志的输出在这个位置。

需要注意的是当我们修改了云对象的代码时,都需要重新点击 debug book

部署云对象

当云对象开发完毕后,想要客户端调用或者上线生产环境,都需要部署上去。右键你的云对象,选择 Deploy 'book' 就可以把你的云对象部署到云端了。你也可以鼠标右键 cloudfouctions 部署或者同步所有的云对象。

如果想要把之前部署到云端的云对象下载下来,选择 Sync book 即可

部署成功,右下角会有相应的提示。

客户端调用云对象

当我们创建好了云对象后,就可以使用客户端直接调用云对象的方法了。

如果是客户端调用云函数,可以参考以下代码。

核心代码如下。

typescript 复制代码
try {
  const res = await cloudFunction.call({
    // 云对象的名称,这个和你创建的云对象文件名一致
    name: "book",
    data: {
      // book云对象的methods1方法
      method: "method1",
      // 传递给method1的参数
      params: ["a", "b"],
    },
  });
  AlertDialog.show({ message: JSON.stringify(res, null, 2) });
} catch (e) {
  console.error(e.message + " " + e.code);
}

这里重点讲解的是客户端调用云对象

DevEco Studio 提供了方便的方式实现客户端调用云对象的功能。可以让我们想调用普通对象方法一样,直接调用云对象!

具体操作步骤如下。

  1. 依据云对象 ,在客户端生成云对象的调用模型。 鼠标右键你的云对象 Book。选择 Generate Invoke Interface

  2. 选择存放该模型文件的路径

  3. 生成成功后,得到两个关键文件 ImportObject.tsBook.ets

    ImportObject.ts文件的主要作用是利用代理 Proxy 的方式,当用户调用Book.method1 方法时,在内部执行了 云函数的调用方法cloudFunction.call方法。为我们调用云对象提供了便利。代码如下。

    typescript 复制代码
    /*
     * Copyright (c) Huawei Technologies Co., Ltd. 2020-2024. All rights reserved.
     * Generated by the Cloud Object compiler. DO NOT EDIT!
     */
    import type { BusinessError } from "@kit.BasicServicesKit";
    import { cloudFunction } from "@kit.CloudFoundationKit";
    
    export interface CloudObjectLikely {
      name: string;
    }
    
    function mockMethod<T extends CloudObjectLikely>(
      target: T,
      version: string,
      prop: string | symbol
    ): (...args: unknown[]) => Promise<unknown> {
      return async (...args: unknown[]) =>
        new Promise((resolve, reject) => {
          cloudFunction
            .call({
              name: target.name,
              version: version,
              data: {
                method: prop,
                params: args,
              },
            })
            .then((value: cloudFunction.FunctionResult) => {
              resolve(value.result);
            })
            .catch((err: BusinessError) => {
              reject(err);
            });
        });
    }
    
    export function importObject<T extends CloudObjectLikely>(
      tClass: new () => T,
      version = "$latest"
    ): T {
      return new Proxy<T>(new tClass(), {
        get(target, prop): (...args: unknown[]) => Promise<unknown> {
          return mockMethod<T>(target, version, prop);
        },
      });
    }

    Book.ets文件的作用是参考云对象 Book 的模型,生成对应的类,方便我们调用云对象。代码如下。

    typescript 复制代码
    /*
     * Copyright (c) Huawei Technologies Co., Ltd. 2020-2024. All rights reserved.
     * Generated by the Cloud Object compiler. DO NOT EDIT!
     */
    import type { CloudObjectLikely } from "../ImportObject";
    
    export class Book implements CloudObjectLikely {
      public name = "book";
      public async method1(
        param1: any,
        param2: any
      ): Promise<{
        simple: string;
        param1: any;
        param2: any;
      }> {
        return Promise.reject(new Error("Method not implemented."));
      }
    }
  4. 最后,在客户端中需要导入 BookimportObject,再进行调用

    jsx 复制代码
    // 这里需要对Book进行重命名,因为和之前的数据库类型Book重名了
    import { Book as BookCloudObj } from "../cloudobject/book/Book";
    import { importObject } from "../cloudobject/ImportObject";
    
    Button("调用云对象14的方法").onClick(this.fn14);
    
    fn14 = async () => {
      try {
        let bookObj = importObject(BookCloudObj); // 使用importObject实例化BookCloudObj的代理
        const res = await bookObj.method1("100", "200");
        AlertDialog.show({ message: JSON.stringify(res, null, 2) });
      } catch (e) {
        console.error(e.message + " " + e.code);
      }
    };
  5. 调用结果如图。

云对象调用云对象

在实际开发中,一般情况下,某个具体的业务是存在某个具体的云对象中的,但是必不可少的会出现业务之间的关联。比如查询某一个作者编写过的所有的书籍。作者可以是一个单独的云对象,书籍也是一个单独的云对象,这样就会出现云对象之间调用云对象 需求。我们可以通过 book 云对象调用 auth 云对象来演示。

由于云对象部署到云端后是各自独立的,因此这里不能简单的将多个云对象看成是同一个目录之间的关系,也就是不能 云对象 a 使用相对路径直接导入云对象 b !

  1. 新建 auth 云对象 这个看上面的创建 book 云对象步骤即可

  2. 调整 auth 云对象的网关设置

    function-config.json 中的authType 设置为 cloudgw-client

    json 复制代码
    "authType": "cloudgw-client"
  3. 编写 auth 云对象逻辑代码

    js 复制代码
    export class Auth {
      getName(firstName, lastName) {
        return { firstName, lastName, fullName: firstName + lastName };
      }
    }
  4. book 安装云对象三方库,注意需要在 book 云对象目录下安装

    sh 复制代码
    npm install @hw-agconnect/cloud-server
  5. book 调用 auth 云对象

    js 复制代码
    import { cloud } from "@hw-agconnect/cloud-server";
    
    export class Book {
      async method1(param1, param2) {
        const res = await cloud.function().call({
          // 云对象的名称
          name: "auth",
    
          data: {
            // 云对象的方法
            method: "getName",
            // 传递的参数
            params: ["万", "大妈"],
          },
        });
    
        return {
          simple: "example",
          param1,
          param2,
          res,
        };
      }
    }
  6. auth 和 book 都部署到云端

  7. 客户端测试调用,验证效果

    支持,便完成了云对象调用云对象了。

云对象调用云存储

在翻阅了相关文档后,发现云对象对于云存储的支持程度远不够,这里略过。

云对象调用云数据库

云对象中也是需要引入 @hw-agconnect/cloud-server 来操作云数据库的。我们按照以下步骤进行操作。实现查询云对象中查

询数据库的功能。

  1. 首先在云对象 book 处新建一个文件夹 model,用来存放即将生成的文件

  2. 在云端生成操作 Book 表格的云对象模块。这里 DevEco Studio 会自动帮我们生成。选择Generate Server Model

    然后选择生成的路径。

  3. 得到文件 model/book.ts ,这里为了和之前的book.ts区分,我们把它名称修改成 model/bookModel.ts。文件中的类名也修改成 BookModel

    typescript 复制代码
    /*
     * Copyright (c) Huawei Technologies Co., Ltd. 2020-2023. All rights reserved.
     * Generated by the CloudDB ObjectType compiler. DO NOT EDIT!
     */
    
    class BookModel {
      id: number;
      name: string;
      price: number;
      publish: Date;
      hot: boolean;
      cover: string;
    
      constructor() {}
    
      getFieldTypeMap(): Map<string, string> {
        let fieldTypeMap = new Map<string, string>();
        fieldTypeMap.set("id", "Integer");
        fieldTypeMap.set("name", "String");
        fieldTypeMap.set("price", "Double");
        fieldTypeMap.set("publish", "Date");
        fieldTypeMap.set("hot", "Boolean");
        fieldTypeMap.set("cover", "String");
        return fieldTypeMap;
      }
    
      getClassName(): string {
        return "Book";
      }
    
      getPrimaryKeyList(): string[] {
        let primaryKeyList: string[] = [];
        primaryKeyList.push("id");
        return primaryKeyList;
      }
    
      getIndexList(): string[] {
        let indexList: string[] = [];
        indexList.push("id");
        indexList.push("price");
        return indexList;
      }
    
      getEncryptedFieldList(): string[] {
        let encryptedFieldList: string[] = [];
        return encryptedFieldList;
      }
    
      setId(id: number): void {
        this.id = id;
      }
    
      getId(): number {
        return this.id;
      }
    
      setName(name: string): void {
        this.name = name;
      }
    
      getName(): string {
        return this.name;
      }
    
      setPrice(price: number): void {
        this.price = price;
      }
    
      getPrice(): number {
        return this.price;
      }
    
      setPublish(publish: Date): void {
        this.publish = publish;
      }
    
      getPublish(): Date {
        return this.publish;
      }
    
      setHot(hot: boolean): void {
        this.hot = hot;
      }
    
      getHot(): boolean {
        return this.hot;
      }
    
      setCover(cover: string): void {
        this.cover = cover;
      }
    
      getCover(): string {
        return this.cover;
      }
    
      static parseFrom(inputObject: any): BookModel {
        let result = new BookModel();
        if (!inputObject) {
          return result;
        }
        if (inputObject.id) {
          result.id = inputObject.id;
        }
        if (inputObject.name) {
          result.name = inputObject.name;
        }
        if (inputObject.price) {
          result.price = inputObject.price;
        }
        if (inputObject.publish) {
          result.publish = new Date(inputObject.publish);
        }
        if (inputObject.hot) {
          result.hot = inputObject.hot;
        }
        if (inputObject.cover) {
          result.cover = inputObject.cover;
        }
        return result;
      }
    }
    
    export { BookModel };
  4. 新建文件bookController.ts,负责组合 对 book 实例的 CURD 操作。

    typescript 复制代码
    import { cloud, CloudDBCollection } from "@hw-agconnect/cloud-server"; //引入Server SDK依赖
    import { BookModel } from "./model/BookModel"; //BookModel为对象类型名
    
    // ZONE_NAME为存储区名称
    const ZONE_NAME = "Study";
    
    export class BookCtroller {
      collection: CloudDBCollection<BookModel>;
    
      constructor() {
        this.collection = cloud
          .database({ zoneName: ZONE_NAME })
          .collection(BookModel);
      }
    
      //查询数据
      async queryBooks() {
        let query = this.collection.query();
        return await query.get();
      }
    
      //更新数据
      async upsertBooks(records: BookModel[]) {
        return await this.collection.upsert(records);
      }
    
      //删除数据
      async deleteBooks(records: BookModel[]) {
        return await this.collection.delete(records);
      }
    }
  5. 最后在云对象 book 中添加一个查询方法 query。

    typescript 复制代码
      // 查询数据库
      async query() {
        let bookCtroller = new BookCtroller();
        const result = await bookCtroller.queryBooks()
        return {
          result
        }
      }
  6. 测试调用

其他的数据库操作可以基于该模式进行拓展。

云对象调用第三方 api

如想要调用其他的 api,比较简单的方式可以直接安装 axios,然后像调用接口一样使用 axios 即可。

总结

  1. 云函数介绍:云函数即 serverless 技术,采用 TypeScript 语法,开发者无需关注服务器相关问题,专注业务开发。它具备调用其他云函数、第三方接口、云存储及云数据库的能力。简单数据库查询等操作可在客户端进行,繁琐或涉及安全的操作适合放在云函数端。
  2. 创建云函数 :建议选择创建云对象,以书籍的 CRUD 为例,将其放在一个对象中更便于管理业务逻辑。创建云对象后会生成 book.ts(编写业务逻辑)、function - config.json(云对象配置,如入口、函数类型、触发器等)、package.json(类似oh - package.json,记录项目信息及依赖)。
  3. 调试云函数 / 对象:在 DevEco Studio 中,右键要调试的云对象选择 "Debug 'book'" 启动调试,可在云对象调试面板和日志面板查看相关信息。修改代码后需重新点击 "debug book"。
  4. 部署云对象:开发完成后,右键云对象选择 "Deploy 'book'" 可部署到云端,也可右键 "cloudfouctions" 部署或同步所有云对象。若要下载已部署的云对象,选择 "Sync book"。
  5. 客户端调用云对象 :DevEco Studio 提供便捷方式,通过右键云对象选择 "Generate Invoke Interface" 生成调用模型,选择存放路径后得到ImportObject.ts(利用代理执行云函数调用方法)和Book.ets(参考云对象模型生成的类)。在客户端导入相关文件后即可调用云对象方法。
  6. 云对象调用云对象 :以 book 云对象调用 auth 云对象为例,需新建 auth 云对象并调整网关设置(function - config.jsonauthType 设为cloudgw - client),编写 auth 云对象逻辑代码,在 book 云对象目录下安装云对象三方库@hw - agconnect/cloud - server,然后在 book 云对象中调用 auth 云对象,最后将两个云对象部署到云端并在客户端测试调用。
  7. 云对象调用云存储:文档显示云对象对云存储支持不足,故略过。
  8. 云对象调用云数据库 :在云对象 book 处新建文件夹 model,通过 DevEco Studio 生成操作 Book 表格的云对象模块,得到bookModel.ts文件并修改相关内容。新建bookController.ts文件负责组合对 book 实例的 CURD 操作,最后在云对象 book 中添加查询方法并进行测试调用。
  9. 云对象调用第三方 api:调用其他 api 较简单的方式是安装 axios,然后像调用接口一样使用。

参考文档

  1. 云侧通过 SDK 使用云数据库
  2. @hw-agconnect/cloud-server
  3. 端云一体化开发

如果你兴趣想要了解更多的鸿蒙应用开发细节和最新资讯,欢迎在评论区留言或者私信或者看我个人信息,可以加入技术交流群。

相关推荐
临江听海16 分钟前
PostgreSql-COALESCE函数、NULLIF函数、NVL函数使用
数据库·postgresql
LLLuckyGirl~17 分钟前
webpack配置之---output.chunkLoadTimeout
前端·webpack·node.js
m0_748239331 小时前
【MySQL】深度学习数据库开发技术:使用CC++语言访问数据库
数据库·mysql·数据库开发
程序员小续1 小时前
现代前端工程化实践:高效构建的秘密
开发语言·前端·javascript·vue.js·webpack·前端框架·ecmascript
RickZhou1 小时前
React 个人博客 支持自定义主题
前端
林啾啾2 小时前
按钮凸起与按下css效果
前端·css
年纪轻轻只想躺平2 小时前
scss模块化
前端·css·scss
浪浪山小白兔2 小时前
CSS 实现下拉菜单效果实例解析
前端·css
世间一剑2 小时前
轻松理解CSS中的float浮动元素
前端·css
大模型铲屎官2 小时前
从基础到进阶,掌握 CSS 变量与calc()函数的完整指南
前端·css·html·css3·html5·css变量·calc函数