Dfinity 基础开发教程
教程概述
快速启动提供了没有停下来欣赏沿途的风景部署简单的默认应用程序快速路径。
此文章介绍了特定场景,并指出了在每个步骤中执行的操作和详细信息。
如果快速入门和教程不完全符合您的风格,此概览备忘单总结了要遵循的步骤以供快速参考。
安装 DFINITY Canister SDK 后,您需要了解以下所有信息:
-
创建一个新项目并切换到项目目录。
dfx new <project_name> && cd <project_name>>
-
编辑定义您的服务或应用程序的后端。
-
编辑为您的服务或应用程序提供前端的 HTML、JavaScript 和 CSS。
-
启动 Internet 计算机进行本地开发或检查与 Internet 计算机的连接以进行网络部署。
-
在本地或网络上注册、构建和部署。
dfx deploy --network <network>
-
在浏览器中查看您的服务或应用程序。
探索默认项目
如果你开始使用DFINITY SDK并快速启动,你已经看到,用于创建Internet 计算机上运行的应用程序的基本工作流程。现在,让我们通过探索在创建新项目时添加到工作区的默认文件和文件夹来仔细研究该工作流程。
作为预览,下图说明了在您的计算机上本地运行 Internet 计算机时的开发工作流程。
在开始之前
在开始本教程之前,请验证以下内容:
- 您有互联网连接并可以访问本地 macOS 或 Linux 计算机上的 shell 终端。
node.js
如果你想在你的项目中包含前端开发的默认模板文件,你已经安装了。- 您已经下载并安装了 DFINITY Canister SDK 包
- 如果您使用 Visual Studio Code 作为 IDE,您已经为 Motoko 安装了 Visual Studio Code 插件。
- 您已停止在本地计算机上运行的任何 Internet 计算机网络进程。
完成本教程大约需要 20 分钟。
创建一个新项目
Internet 计算机的应用程序作为您创建的项目启动。您可以使用dfx
可执行命令行界面 (CLI)创建项目。
要仔细查看默认情况下包含在项目中的文件和文件夹,让我们创建一个新项目来使用。
要创建一个新项目:
-
如果您还没有打开,请在本地计算机上打开一个终端外壳。
-
如果您使用单独的工作文件夹,请导航到您用于 Internet 计算机项目的文件夹。
-
通过运行以下命令创建一个新项目:
dfx new explore_hello
该
dfx new explore_hello
命令会创建一个新explore_hello
项目,包括新项目名称下的默认项目目录结构和项目的新 Git 存储库。如果已经node.js
安装在本地,新建项目也会添加一些模板前端代码和依赖。为确保项目名称在 JavaScript、Motoko 和其他上下文中使用时有效,您应该只使用字母数字字符和下划线。不能包含破折号或任何特殊字符。
-
通过运行以下命令查看默认目录结构:
ls -l explore_hello
默认情况下,项目目录结构至少包括一个源子目录、一个模板
README.md
文件和一个默认dfx.json
配置文件。根据您是否已
node.js
安装,您的项目目录可能包含以下部分或全部文件:explore_hello/ ├── README.md # default project documentation ├── dfx.json # project configuration file ├── node_modules # libraries for front-end development ├── package-lock.json ├── package.json ├── src # source files directory │ ├── explore_hello │ │ └── main.mo │ └── explore_hello_assets │ ├── assets │ │ ├── logo.png │ │ ├── main.css │ │ └── sample-asset.txt │ └── src │ ├── index.html │ └── index.js └── webpack.config.js
默认项目目录至少包括以下文件夹和文件:
README
用于在存储库中记录项目的默认文件。dfx.json
用于为项目设置可配置选项的默认配置文件。src
应用程序所需的所有源文件的默认目录。
默认
src
目录包含一个模板main.mo
文件,您可以修改或替换该文件以包含您的核心编程逻辑。由于本教程侧重于入门的基础知识,因此您将只使用该
main.mo
文件。如果您已node.js
安装,您的项目目录将包含其他文件和目录,您可以使用它们来定义应用程序的前端界面。前端开发和文件assets
夹中的模板文件稍后讨论。
查看默认配置
默认情况下,创建新项目会将一些模板文件添加到您的项目目录中。您可以编辑这些模板文件以自定义项目的配置设置并包含您自己的代码以加快开发周期。
要查看项目的默认配置文件:
-
如果您还没有打开,请在本地计算机上打开一个终端外壳。
-
通过运行以下命令切换到您的项目目录:
cd explore_hello
-
dfx.json
在文本编辑器中打开配置文件以查看默认设置。例如:
{ "canisters": { "explore_hello": { "main": "src/explore_hello/main.mo", "type": "motoko" }, "explore_hello_assets": { "dependencies": [ "explore_hello" ], "frontend": { "entrypoint": "src/explore_hello_assets/src/index.js" }, "source": [ "src/explore_hello_assets/assets", "dist/explore_hello_assets/" ], "type": "assets" } }, "defaults": { "build": { "packtool": "" } }, "dfx": "0.7.2", "networks": { "local": { "bind": "127.0.0.1:8000", "type": "ephemeral" } }, "version": 1 }
让我们来看看一些默认设置。
-
该
canisters
部分指定explore_hello
项目的 WebAssembly 模块的名称是explore_hello
. -
所述
canisters.explore_hello
key指定要编译的主程序位于路径中指定由所述main
设置,在这种情况下,src/explore_hello/main.mo
与type
设置表示这是一个motoko
程序。 -
在
canisters.explore_hello_assets
关于这个项目的前端资产密钥指定配置细节。让我们暂时跳过这些。 -
该
dfx
设置用于标识用于创建项目的软件版本。 -
该
networks
部分指定有关您连接到的网络的信息。默认设置将本地 Internet 计算机网络绑定到本地主机地址127.0.0.1
和端口8000
。如果您可以访问其他 Internet 计算机网络提供商,则该
networks
部分可以包含用于连接到这些提供商的网络别名和 URL。
您可以保留默认设置。
-
-
关闭
dfx.json
文件以继续。
查看默认程序代码
新项目总是包含一个模板main.mo
源代码文件。您可以编辑此文件以包含您自己的代码以加快开发周期。
让我们看一下默认main.mo
模板文件中的示例程序,作为使用 Motoko 编程语言创建简单程序的起点。
要查看项目的默认示例程序:
-
通过运行以下命令检查您是否仍在项目目录中:
pwd
-
src/explore_hello/main.mo
在文本编辑器中打开文件并查看模板中的代码:actor { public func greet(name : Text) : async Text { return "Hello, " # name # "!"; }; };
让我们来看看这个程序的几个关键元素:
- 您可能会注意到,此示例代码定义了一个
actor
而不是main
某些编程语言需要的函数。对于 Motoko,该main
函数隐含在文件本身中。 - 虽然传统的“Hello, World!” 程序说明了如何使用
print
或println
函数打印字符串,该传统程序不能代表在 Internet 计算机上运行的 Motoko 程序的典型用例。 - 这个示例程序定义了一个
actor
with 公共greet
函数,而不是一个打印函数,它接受name
一个类型为的参数Text
。 - 然后程序使用
async
关键字来指示程序返回一个异步消息,该消息由使用"Hello, "
、#
运算符、name
参数和构造的串联文本字符串组成"!"
。
稍后我们将更多地探索使用
actor
对象和异步消息处理的代码。现在,您可以继续下一部分。 - 您可能会注意到,此示例代码定义了一个
-
关闭
main.mo
文件以继续。
启动本地网络
在构建默认项目之前,您需要连接到 Internet 计算机网络,要么在您的开发环境中本地运行,要么在您可以访问的子网上远程运行。
在本地启动网络需要一个dfx.json
文件,因此您应该确保您位于项目的根目录中。对于本教程,您应该有两个独立的终端外壳,以便您可以在一个终端中启动和查看网络操作,并在另一个终端中管理您的项目。
在本地启动网络:
-
在本地计算机上打开一个新的终端窗口或一个新的终端选项卡。
-
如有必要,导航到项目的根目录。
- 您现在应该打开了两个终端。
- 您应该将项目目录作为您当前的工作目录。
-
通过运行以下命令在本地计算机上启动 Internet 计算机网络:
dfx start
根据您的平台和本地安全设置,您可能会看到显示的警告。如果系统提示您允许或拒绝传入网络连接,请单击允许。
启动本地网络后,您有一个终端显示有关网络操作的消息,另一个终端用于执行与项目相关的任务。
-
让显示网络操作的终端保持打开状态,并将焦点切换到您创建新项目的终端。
注册容器标识符
在您连接到在您的开发环境中本地运行的 Internet 计算机网络后,您可以向网络注册,以便为您的项目生成唯一的、特定于网络的容器标识符。
在快速入门教程中,此步骤是作为dfx deploy
命令工作流程的一部分执行的。本教程演示了如何独立执行每个操作。
要为本地网络注册容器标识符:
-
如果需要,请检查您是否仍在项目目录中。
-
通过运行以下命令为项目中的容器注册唯一的容器标识符:
dfx canister create --all
该命令显示
dfx.json
配置文件中定义的容器的特定于网络的容器标识符。Creating a wallet canister on the local network. The wallet canister on the "local" network for user "pubs-id" is "rwlgt-iiaaa-aaaaa-aaaaa-cai" Creating canister "explore_hello"... "explore_hello" canister created with canister id: "rrkah-fqaaa-aaaaa-aaaaq-cai" Creating canister "explore_hello_assets"... "explore_hello_assets" canister created with canister id: "ryjl3-tyaaa-aaaaa-aaaba-cai"
由于您已连接到本地运行的 Internet 计算机网络,因此这些容器标识符仅在本地有效,并针对项目存储在
.dfx/local/canister_ids.json
文件中。例如:
{ "explore_hello": { "local": "rrkah-fqaaa-aaaaa-aaaaq-cai" }, "explore_hello_assets": { "local": "ryjl3-tyaaa-aaaaa-aaaba-cai" } }
编译程序
现在您已经探索了默认配置设置和程序代码并启动了 Internet Computer 网络,让我们将默认程序编译成一个可执行的 WebAssembly 模块。
要构建程序可执行文件:
-
在本地计算机上的终端 shell 中,导航到您的
explore_hello
项目目录。您必须
dfx build
从项目目录结构中运行该命令。 -
通过运行以下命令构建可执行容器:
dfx build
您应该会看到类似于以下内容的输出:
Building canisters... Building frontend...
由于您连接到本地运行的 Internet 计算机网络,因此该
dfx build
命令会在项目canisters
目录下添加目录.dfx/local/
。 -
通过运行以下命令验证
.dfx/local/canisters/explore_hello
该dfx build
命令创建的目录是否包含 WebAssembly 和相关应用程序文件。ls -l .dfx/local/canisters/explore_hello/
例如,该命令返回类似于以下内容的输出:
-rw-r--r-- 1 pubs staff 178 Apr 6 14:25 explore_hello.d.ts -rw-r--r-- 1 pubs staff 41 Apr 6 14:25 explore_hello.did -rw-r--r-- 1 pubs staff 155 Apr 6 14:25 explore_hello.did.js -rw-r--r-- 1 pubs staff 142 Apr 6 14:25 explore_hello.js -rw-r--r-- 1 pubs staff 157613 Apr 6 14:25 explore_hello.wasm
该
canisters/explore_hello
目录包含以下关键文件:- 该
explore_hello.did
文件包含主程序的接口描述。 - 该
explore_hello.did.js
文件包含程序中函数的容器接口的 JavaScript 表示。 - 该
explore_hello.js
文件包含程序的容器接口的 JavaScript 表示。 - 该
explore_hello.wasm
文件包含为项目中使用的资产编译的 WebAssembly。
该
canisters/explore_hello_assets
目录包含用于描述与您的项目相关联的前端资产的类似文件。除了
canisters/explore_hello
和canisters/explore_hello_assets
目录中的文件外,该dfx build
命令还会创建一个idl
目录。 - 该
在本地部署项目
您已经看到该dfx build
命令在canisters
您的项目的目录中创建了多个工件。canister_manifest.json
要在 Internet 计算机网络上部署您的程序,需要WebAssembly 模块和文件。
本地部署:
-
在本地计算机上的终端 shell 中,导航到您的
explore_hello
项目目录。 -
explore_hello
通过运行以下命令在本地网络上部署您的项目:dfx canister install --all
该命令显示类似于以下内容的输出:
Installing code for canister explore_hello, with canister_id rrkah-fqaaa-aaaaa-aaaaq-cai Installing code for canister explore_hello_assets, with canister_id ryjl3-tyaaa-aaaaa-aaaba-cai Authorizing our identity (pubs-id) to the asset canister... Uploading assets to asset canister... /index.html 1/1 (480 bytes) /index.js 1/1 (296836 bytes) /main.css 1/1 (484 bytes) /sample-asset.txt 1/1 (24 bytes) /logo.png 1/1 (25397 bytes) /index.js.map 1/1 (964679 bytes) /index.js.LICENSE.txt 1/1 (499 bytes)
-
运行
dfx canister call
命令并通过运行以下命令指定要调用的程序和函数:dfx canister call explore_hello greet everyone
此命令指定:
explore_hello
作为您要调用的容器或应用程序服务的名称。greet
作为您要调用的特定方法或函数。everyone
作为传递给greet
函数的参数。
-
验证命令显示
greet
函数的返回值。例如:
("Hello, everyone!")
查看默认前端
如果您已node.js
安装在您的开发环境中,您的项目将包含一个简单的前端示例,该示例使用模板index.js
JavaScript 文件explore_hello
在浏览器中访问该程序。
要探索默认的前端模板:
-
在本地计算机上打开一个终端 shell(如果您还没有打开),然后导航到您的
explore_hello
项目目录。 -
src/explore_hello_assets/src/index.js
在文本编辑器中打开文件并查看模板脚本中的代码:import { Actor, HttpAgent } from '@dfinity/agent'; import { idlFactory as explore_hello_idl, canisterId as explore_hello_id } from 'dfx-generated/explore_hello'; const agent = new HttpAgent(); const explore_hello = Actor.createActor(explore_hello_idl, { agent, canisterId: explore_hello_id }); document.getElementById("clickMeBtn").addEventListener("click", async () => { const name = document.getElementById("name").value.toString(); const greeting = await explore_hello.greet(name); document.getElementById("greeting").innerText = greeting; });
模板
index.js
文件使用两个import
语句显式创建代理实例和explore_hello
由dfx
.此文件与模板
index.html
文件结合使用以显示带有图像资产、输入字段和greet
函数按钮的 HTML 页面。 -
关闭
index.js
文件以继续。 -
通过运行以下命令查看为项目创建的前端资产:
ls -l .dfx/local/canisters/explore_hello_assets/
该命令显示类似于以下内容的输出:
drwxr-xr-x 9 pubs staff 288 Apr 6 14:25 assets -r--r--r-- 1 pubs staff 2931 Dec 31 1969 assetstorage.did -r--r--r-- 1 pubs staff 265823 Dec 31 1969 assetstorage.wasm -rw-r--r-- 1 pubs staff 3651 Apr 6 14:25 explore_hello_assets.d.ts -rw-rw-rw- 1 pubs staff 2931 Dec 31 1969 explore_hello_assets.did -rw-r--r-- 1 pubs staff 4236 Apr 6 14:25 explore_hello_assets.did.js -rw-r--r-- 1 pubs staff 149 Apr 6 14:25 explore_hello_assets.js -rw-rw-rw- 1 pubs staff 265823 Dec 31 1969 explore_hello_assets.wasm
这些文件是由
dfx build
命令使用节点模块和模板index.js
文件自动生成的。 -
打开浏览器,然后导航到
local
网络地址和端口号-127.0.0.1:8000/
中-specifieddfx.json
配置文件。要指定您希望 Web 服务器显示
canisterId
的explore_hello_assets
容器,请使用以下语法将参数和 容器标识符添加到 URL:?canisterId=<YOUR-CANISTER-IDENTIFIER>
例如,完整的 URL 应类似于以下内容:
http://127.0.0.1:8000/?canisterId=rrkah-fqaaa-aaaaa-aaaaq-cai
-
验证您是否看到了示例应用程序的 HTML 页面。
例如:
- 键入问候语,然后单击“**单击我”**以返回问候语。
停止本地网络
在您完成程序的试验后,您可以停止本地 Internet 计算机网络,使其不会继续在后台运行。
要停止本地网络:
-
在显示网络操作的终端中,按 Control-C 可中断本地网络进程。
-
通过运行以下命令停止 Internet 计算机网络:
dfx stop
使用 actor 查询
在快速入门中,您第一次看到了一个简单的 Internet 计算机程序,其中包含一个参与者对象和异步消息传递。作为学习编写利用基于参与者的消息传递的程序的下一步,本教程说明了如何修改传统Hello, World!
程序以定义参与者,然后在本地网络上部署和测试您的程序。
在你开始之前
在开始本教程之前,请验证以下内容:
- 您已经下载并安装了 DFINITY Canister SDK 包,
- 您已停止在本地计算机上运行的任何 Internet 计算机网络进程。
完成本教程大约需要 20 分钟。
创建一个新项目
要为本教程创建一个新项目:
-
如果您还没有打开,请在本地计算机上打开一个终端外壳。
-
如果您正在使用 Internet 计算机项目,请切换到您用于 Internet 计算机项目的文件夹。
-
通过运行以下命令创建一个新项目:
dfx new actor_hello
-
通过运行以下命令切换到您的项目目录:
cd actor_hello
修改默认配置
您看到创建新项目会将默认dfx.json
配置文件添加到您的项目目录中。在本教程中,您需要修改一些默认设置以反映您的项目。
修改dfx.json
配置文件:
-
dfx.json
在文本编辑器中打开配置文件。 -
检查
actor_hello
项目的默认设置。 -
请注意,源文件和输出文件的名称和路径都使用
actor_hello
项目名称。例如,默认容器名称是
actor_hello
,主程序文件的默认路径是src/actor_hello/main.mo
.您可以重命名这些文件或目录中的任何一个。但是,如果您进行任何更改,请确保您用于文件系统上的文件和目录的名称与您在
dfx.json
配置文件中指定的名称相匹配。如果您计划使用默认目录和文件名,则无需更改。 -
actor_hello_assets
从文件中删除所有配置设置。本教程的示例程序不使用任何前端资产,因此您可以从配置文件中删除这些设置。
例如,删除该
actor_hello_assets
部分后的配置文件如下所示:{ "canisters": { "actor_hello": { "main": "src/actor_hello/main.mo", "type": "motoko" } }, "defaults": { "build": { "packtool": "" } }, "dfx": "0.7.2", "networks": { "local": { "bind": "127.0.0.1:8000", "type": "ephemeral" } }, "version": 1 }
-
保存更改并关闭文件以继续。
修改默认程序
您看到创建新项目会创建一个src
带有模板main.mo
文件的默认目录。在本教程中,您将修改模板代码以创建一个简单的“Hello, World!” 使用演员的程序。
修改默认模板源代码:
-
通过运行以下命令切换到项目的源代码目录:
cd src/actor_hello
-
main.mo
在文本编辑器中打开模板文件并删除现有内容。下一步是编写一个程序,打印一个类似于传统的“Hello, World!”语句的程序。示例程序。但是,要为 Internet 计算机编译程序,您的程序必须包含一个
actor
具有public
函数的对象。 -
将以下示例代码复制并粘贴到
main.mo
文件中:import Debug "mo:base/Debug"; actor HelloActor { public query func hello() : async () { Debug.print ("Hello, World from DFINITY \n"); } };
让我们仔细看看这个简单的程序:
- 该程序导入一个
Debug
模块以提供print
功能。 - 该程序使用
public query func
来定义查询方法,因为在这种情况下,actor_hello
程序不会对容器的状态进行任何更改,也不会执行任何会更新您正在访问的数据的操作。
- 该程序导入一个
-
保存更改并关闭
main.mo
文件。
使用本地标识符构建程序
您可能只会将这个简单的程序用于一些本地测试。因此,无需在 Internet 计算机网络上保留唯一的容器标识符来保存程序的编译输出。
在这种情况下,您可以在完全不连接 Internet 计算机网络的情况下编译程序。相反,该dfx build
命令会创建一个本地、硬编码的容器标识符供您使用。
您可以在测试您的程序时或任何时候您想编译您的程序时使用此本地标识符,而无需在本地启动 Internet 计算机副本进程或连接到远程子网上的副本。
要构建程序可执行文件:
-
导航回项目目录的根目录。
-
通过运行以下命令,使用本地定义的标识符构建程序:
dfx build --check
该
--check
选项使您能够在本地构建项目以验证它是否编译并检查生成的文件。由于该dfx build --check
命令仅生成一个临时标识符,您应该会看到类似于以下内容的输出:Building canisters to check they build ok. Canister IDs might be hard coded. Building canisters...xxxxxxxxxx Building canisters to check they build ok. Canister IDs might be hard coded.Building canisters...
如果程序编译成功,您可以检查默认
.dfx/local/canisters
目录和.dfx/local/canisters/actor_hello/
子目录中的输出。例如,您可以使用以下
tree
命令查看创建的文件:tree .dfx/local/canisters
该命令显示类似于以下内容的输出
.dfx/local/canisters
├── actor_hello
│ ├── actor_hello.d.ts
│ ├── actor_hello.did
│ ├── actor_hello.did.js
│ ├── actor_hello.js
│ └── actor_hello.wasm
└── idl
2 directories, 5 files
部署项目
您不能将dfx build --check
命令的输出部署到任何 Internet 计算机网络。如果要部署此项目,则需要执行以下操作:
- 连接到 Internet 计算机网络。
- 注册特定于网络的容器标识符。
- 部署容器。
让我们更详细地考虑这些步骤。在部署此项目之前,您必须连接到 Internet 计算机网络,要么在您的开发环境中本地运行,要么在您可以访问的子网上远程运行。在连接到本地或远程网络后,您还必须生成一个唯一的、特定于网络的容器标识符来替换本地定义的标识符。要自己查看所涉及的步骤,让我们将项目部署到本地。
要在本地部署此项目:
-
如果需要,打开终端并导航到您的项目目录。
-
通过运行以下命令在本地计算机上启动 Internet 计算机网络:
dfx start --background
对于本教程,您可以使用该
--background
选项将 Internet 计算机网络作为后台进程启动。使用此选项,您可以继续下一步,而无需在本地计算机上打开另一个终端外壳。 -
通过运行以下命令,在本地 Internet 计算机网络上为您的项目生成新的容器标识符:
dfx canister create actor_hello
您应该会看到类似于以下内容的输出:
Creating a wallet canister on the local network. The wallet canister on the "local" network for user "pubs-id" is "rwlgt-iiaaa-aaaaa-aaaaa-cai" Creating canister "actor_hello"... "actor_hello" canister created with canister id: "rrkah-fqaaa-aaaaa-aaaaq-cai"
该
dfx canister create
命令还将特定于网络的容器标识符canister_ids.json
存储在.dfx/local
目录中的文件中。例如:
{ "actor_hello": { "local": "rrkah-fqaaa-aaaaa-aaaaq-cai" } }
-
actor_hello
通过运行以下命令在本地网络上部署您的项目:dfx canister install actor_hello
该命令显示类似于以下内容的输出:
Installing code for canister actor_hello, with canister_id rrkah-fqaaa-aaaaa-aaaaq-cai
查询容器
您现在已将程序部署为本地 Internet 计算机网络上的容器,并且可以使用该dfx canister call
命令测试您的程序。
要测试您在本地网络上部署的程序:
-
用于通过运行以下命令
dfx canister call
来调用该hello
函数:dfx canister call actor_hello hello
-
验证命令返回为
hello
函数指定的文本以及运行本地网络进程的终端中的检查点消息。例如,程序在类似于以下的输出中显示“Hello, World from DFINITY”:
[Canister rrkah-fqaaa-aaaaa-aaaaq-cai] Hello, World from DFINITY
请注意,如果您在单独的终端而不是在后台运行 Internet 计算机网络,则会在显示网络活动的终端中显示“Hello, World from DFINITY”消息。
停止本地网络
在您完成程序的试验后,您可以停止本地 Internet 计算机网络,使其不会继续在后台运行。
要停止本地网络:
-
在显示网络操作的终端中,按 Control-C 可中断本地网络进程。
-
通过运行以下命令停止 Internet 计算机网络:
dfx stop
传递文本参数
本教程提供了默认程序的一个简单变体,它允许您将单个文本参数传递给单个 actor,编译代码以创建一个容器,然后检索该参数。
本教程说明了如何使用 Candid 接口描述语言 (IDL) 在终端的命令行上传递参数,以及如何修改程序以允许它接受多个文本参数值。
在你开始之前
在开始本教程之前,请验证以下内容:
- 您已经下载并安装了 DFINITY Canister SDK 包
- 您已停止在本地计算机上运行的任何 Internet 计算机网络进程。
完成本教程大约需要 20 分钟。
创建一个新项目
要为本教程创建一个新项目:
-
如果您还没有打开,请在本地计算机上打开一个终端外壳。
-
如果您正在使用 Internet 计算机项目,请切换到您用于 Internet 计算机项目的文件夹。
-
通过运行以下命令创建一个新项目:
dfx new location_hello
-
通过运行以下命令切换到您的项目目录:
cd location_hello
修改默认配置
创建新项目会将默认dfx.json
配置文件添加到您的项目目录中。您应该始终查看文件中的默认设置,以验证信息是否准确反映了您要使用的项目设置。对于本教程,您将修改默认配置以删除未使用的设置。
要修改dfx.json
配置文件中的设置:
-
dfx.json
在文本编辑器中打开配置文件。 -
检查
location_hello
项目的默认设置。 -
删除所有不必要的配置设置。
由于本教程不涉及创建任何前端资产,因此您可以
location_hello_assets
从文件中删除所有配置设置。 -
保存更改并关闭文件以继续。
修改默认程序
您看到创建新项目会创建一个src
带有模板main.mo
文件的默认目录。
修改默认模板源代码:
-
src/location_hello/main.mo
在文本编辑器中打开源代码文件。 -
修改默认源代码,用
greet
函数替换location
函数,用name
参数替换city
参数。例如:
actor { public func location(city : Text) : async Text { return "Hello, " # city # "!"; }; };
-
保存更改并关闭文件以继续。
启动本地网络
在构建项目之前,您需要连接到 Internet 计算机网络,要么在您的开发环境中本地运行,要么在您可以访问的子网上远程运行。
在本地启动网络需要一个dfx.json
文件,因此您应该确保您位于项目的根目录中。对于本教程,您应该有两个独立的终端外壳,以便您可以在一个终端中启动和查看网络操作,并在另一个终端中管理您的项目。
要启动本地网络:
-
在本地计算机上打开一个新的终端窗口或选项卡。
-
如有必要,导航到项目的根目录。
- 您现在应该打开了两个终端。
- 您应该将项目目录作为您当前的工作目录。
-
通过运行以下命令在本地计算机上启动 Internet 计算机网络:
dfx start
如果系统提示您允许或拒绝传入网络连接,请单击允许。
-
让显示网络操作的终端保持打开状态,并将焦点切换到您创建项目的原始终端。
注册、构建和部署应用程序
在您连接到在您的开发环境中本地运行的 Internet 计算机网络后,您可以在本地注册、构建和部署您的应用程序。
在本地部署应用程序:
-
如果需要,请检查您是否仍在项目的根目录中。
-
通过运行以下命令注册、构建和部署您的应用程序:
dfx deploy
的
dfx deploy
关于它执行的操作命令输出显示信息。
传递文本参数
您现在已将程序部署为本地 Internet 计算机网络上的容器,并且可以使用dfx canister call
命令测试您的程序。
要测试您在本地部署的程序:
-
调用
location
程序中的方法并通过运行以下命令传递city
类型参数text
:dfx canister call location_hello location "San Francisco"
由于本例中的参数在
San
和之间包含一个空格Francisco
,因此您需要将参数括在引号中。该命令显示类似于以下内容的输出:("Hello, San Francisco!")
如果参数不包含需要将文本括在引号内的空格,您可以允许 Candid 接口描述语言推断数据类型,如下所示:
dfx canister call location_hello location Paris
Candid 推断数据类型为
Text
,并将程序的输出作为文本返回,如下所示:("Hello, Paris!")
-
调用
location
程序中的方法并city
使用文本参数的 Candid 接口描述语言语法显式传递参数:dfx canister call location_hello location '("San Francisco and Paris")'
该命令显示类似于以下内容的输出:
("Hello, San Francisco and Paris!")
因为您的程序只接受一个文本参数,所以指定多个字符串只会返回第一个参数。
例如,如果您尝试此命令:
dfx canister call location_hello location '("San Francisco","Paris","Rome")'
仅
("Hello, San Francisco!")
返回第一个参数— — 。
修改程序中的源代码
为了扩展您在本教程中学到的知识,您可能想要尝试修改源代码以返回不同的结果。例如,您可能希望修改location
函数以返回多个城市名称。
要尝试修改本教程的源代码:
-
dfx.json
在文本编辑器中打开配置文件并将默认location_hello
设置更改为favorite_cities
.对于这一步,您应该修改容器名称和主程序的路径,以便容器使用
favorite_cities
。 -
保存更改并关闭
dfx.json
文件以继续。 -
通过运行以下命令复制
location_hello
源文件目录以匹配dfx.json
配置文件中指定的名称:cp -r src/location_hello src/favorite_cities
-
src/favorite_cities/main.mo
在文本编辑器中打开文件。 -
复制并粘贴以下代码示例以
location
使用两个新函数替换该函数。例如:
actor { public func location(cities : [Text]) : async Text { return "Hello, from " # (debug_show cities) # "!"; }; public func location_pretty(cities : [Text]) : async Text { var str = "Hello from "; for (city in cities.vals()) { str := str # city #", "; }; return str # "bon voyage!"; } };
您可能注意到
Text
在此代码示例中用方 ([ ]
) 括号括起来。就其本身而言,Text
代表一组 UTF-8 字符。类型周围的方括号表示它是该类型的数组。因此,在此上下文中,[Text]
表示一组 UTF-8 字符集合,使程序能够接受和返回多个文本字符串。代码示例还使用了
apply
数组操作的基本格式,可以抽象为:public func apply<A, B>(fs : [A -> B], xs : [A]) : [B] { var ys : [B] = []; for (f in fs.vals()) { ys := append<B>(ys, map<A, B>(f, xs)); }; ys; };
有关对数组执行操作的函数的信息,请参阅 Motoko 基础库或Motoko 编程语言参考中对 Array 模块的描述
-
通过运行以下命令注册、构建和部署应用程序:
dfx deploy
-
通过运行以下命令,调用
location
程序中的方法并city
使用 Candid 接口描述语法传递参数:dfx canister call favorite_cities location '(vec {"San Francisco";"Paris";"Rome"})'
该命令使用 Candid 接口描述语法
(vec { val1; val2; val3; })
返回值向量。有关 Candid 界面描述语言的更多信息,请参阅Candid语言指南。此命令显示类似于以下内容的输出:
dfx canister call favorite_cities location '(vec {"San Francisco";"Paris";"Rome"})'
-
通过运行以下命令调用
location_pretty
程序中的方法并city
使用接口描述语法传递参数:dfx canister call favorite_cities location_pretty '(vec {"San Francisco";"Paris";"Rome"})'
该命令显示类似于以下内容的输出:
("Hello from San Francisco, Paris, Rome, bon voyage!")
在浏览器中测试功能
容器接口描述语言(通常称为 Candid 或更一般地称为 IDL)提供了一种用于指定容器服务签名的通用语言。Candid 为您提供了一种统一的方式来与用不同语言编写或使用不同工具访问的容器进行交互。例如,无论底层程序是原生 Rust、JavaScript 还是 Motoko,Candid 都提供一致的服务视图。Candid 还支持不同的工具(例如dfx
命令行界面和网络神经系统应用程序)来共享服务的通用描述。
基于 actor 的类型签名,Candid 还提供了一个 Web 界面,允许您调用容器函数进行测试和调试。
使用dfx deploy
ordfx canister install
命令在本地部署项目后,您可以在浏览器中访问 Candid Web 界面端点。此 Web 界面(Candid UI)以表单形式公开服务描述,使您能够快速查看和测试功能并尝试输入不同的数据类型,而无需编写任何前端代码。
要使用 Candid Web 界面来测试容器功能:
-
使用
dfx canister id __Candid_UI
命令查找与当前项目关联的 Candid UI 容器标识符。dfx canister id __Candid_UI
该命令显示 Candid UI 的容器标识符,输出类似于以下内容:
r7inp-6aaaa-aaaaa-aaabq-cai
-
复制 Candid UI 容器标识符,以便它在剪贴板中可用。
-
如果您已停止 Internet 计算机,请通过运行以下命令在本地重新启动它:
dfx start --background
-
打开浏览器并导航到
dfx.json
配置文件中指定的地址和端口号。默认情况下,
local
网络绑定到127.0.0.1:8000
地址和端口号。 -
canisterId
将dfx canister id
命令返回的必需参数和 Candid UI 容器标识符添加到 URL。例如,完整的 URL 应该类似于以下内容,但带有
CANDID-UI-CANISTER-IDENTIFIER
由dfx canister id
命令返回的:http://127.0.0.1:8000/?canisterId=<CANDID-UI-CANISTER-IDENTIFIER>
浏览器会显示一个表单,供您指定容器标识符或选择 Candid 描述 (
.did
) 文件。如果您不确定要使用哪个容器标识符,您可以运行该
dfx canister id
命令来查找特定容器名称的标识符。 -
在提供容器 ID字段中为您的应用程序指定容器标识符或描述文件,然后单击转到以显示服务描述。
-
查看程序中定义的函数调用和类型列表。
-
为函数键入适当类型的值,或单击“**随机”**生成值,然后单击“**调用”**或“**查询”**以查看结果。
请注意,根据数据类型,Candid 界面可能会显示用于测试功能的其他配置设置。例如,如果函数接受一个数组,您可能需要在输入值之前指定数组中的项数。
在这个例子中,每个函数都接受一个文本字符串数组。因此,您首先选择数组的长度,然后在单击Call之前为每个项目设置值。
停止本地网络
在您完成程序的试验后,您可以停止本地 Internet 计算机网络,使其不会继续在后台运行。
要停止本地网络:
-
在显示网络操作的终端中,按 Control-C 可中断本地网络进程。
-
通过运行以下命令停止 Internet 计算机网络:
dfx stop
增加一个自然数
在本教程中,您将编写一个程序来创建单个参与者并提供一些基本函数来增加计数器并说明值的持久性。
在本教程中,角色名为Counter
。程序使用该currentValue
变量来包含一个代表计数器当前值的自然数。该程序支持以下函数调用:
- 该
increment
函数调用更新当前值,由1(无返回值)递增。 - 该
get
函数调用查询并返回当前值。 - 该
set
函数调用更新当前值到您指定作为参数的任意数值。
本教程提供了一个简单示例,说明如何通过调用已部署容器上的函数来增加计数器。通过多次调用该函数来增加一个值,您可以验证变量状态(即两次调用之间的变量值)是否持续存在。
在你开始之前
在开始本教程之前,请验证以下内容:
- 您已经下载并安装了 DFINITY Canister SDK 包。。
- 您已停止在本地计算机上运行的任何 Internet 计算机网络进程。
完成本教程大约需要 20 分钟。
创建一个新项目
要为本教程创建新的项目目录:
-
如果您还没有打开,请在本地计算机上打开一个终端外壳。
-
如果您正在使用 Internet 计算机项目,请切换到您用于 Internet 计算机项目的文件夹。
-
通过运行以下命令创建一个新项目:
dfx new my_counter
该命令
my_counter
为您的项目创建一个新项目和 Git 存储库。 -
通过运行以下命令切换到您的项目目录:
cd my_counter
修改默认配置
你已经看到,创建一个新的项目增加了一个默认dfx.json
的配置文件到您的项目目录。在本教程中,您将修改默认设置,使用不同的名称在您的项目主程序。
修改默认dfx.json
配置文件:
-
打开
dfx.json
在文本编辑器配置文件,更改默认main
的设置main.mo
来increment_counter.mo
。例如:
"main": "src/my_counter/increment_counter.mo",
对于本教程,将源文件的名称从
main.mo
更改为increment_counter.mo
仅说明dfx.json
配置文件中的设置如何确定要编译的源文件。在更复杂的应用程序中,您可能有多个带有依赖项的源文件,您需要使用
dfx.json
配置文件中的设置来管理这些依赖项。在这样的场景中——在你的dfx.json
文件中定义了多个容器和程序——同时命名多个文件main.mo
可能会令人困惑。您可以保留其余的默认设置。
-
保存更改并关闭
dfx.json
文件以继续。 -
dfx.json
通过运行以下命令将源代码目录中的主程序文件的名称更改为与配置文件中指定的名称匹配mv src/my_counter/main.mo src/my_counter/increment_counter.mo
修改默认程序
到目前为止,您只更改了项目的主程序的名称。下一步是修改代码src/my_counter/increment_counter.mo
文件中定义了一个名为一名演员Counter
和落实increment
,get
以及set
功能。
修改默认模板源代码:
-
如果需要,请检查您是否仍在项目目录中。
-
src/my_counter/increment_counter.mo
在文本编辑器中打开文件并删除现有内容。 -
将以下示例代码复制并粘贴到
increment_counter.mo
文件中:// Create a simple Counter actor. actor Counter { stable var currentValue : Nat = 0; // Increment the counter with the increment function. public func increment() : async () { currentValue += 1; }; // Read the counter value with a get function. public query func get() : async Nat { currentValue }; // Write an arbitrary value with a set function. public func set(n: Nat) : async () { currentValue := n; }; }
跑步
让我们仔细看看这个示例程序:
-
您可以看到
currentValue
此示例中的变量声明包含stable
用于指示状态的关键字——可以设置、递增和检索的值——保持不变。该关键字确保程序升级时变量的值不变。
-
currentValue
变量的声明还指定其类型是自然 (Nat
) 数。 -
该程序包括两个公共更新方法——the
increment
和set
函数——以及一个查询方法,在本例中为get
函数。
-
-
保存更改并关闭文件以继续。
启动本地网络
在构建my_counter
项目之前,您需要连接到 Internet 计算机网络,要么在您的开发环境中本地运行,要么在您可以访问的子网上远程运行。
在本地启动网络需要一个dfx.json
文件,因此您应该确保您位于项目的根目录中。对于本教程,您应该有两个独立的终端外壳,以便您可以在一个终端中启动和查看网络操作,并在另一个终端中管理您的项目。
在本地启动网络:
-
在本地计算机上打开一个新的终端窗口或选项卡。
-
如有必要,导航到项目的根目录。
- 您现在应该打开了两个终端。
- 您应该将项目目录作为您当前的工作目录。
-
通过运行以下命令在本地计算机上启动 Internet 计算机网络:
dfx start
启动本地网络后,终端会显示有关网络操作的消息。
-
让显示网络操作的终端保持打开状态,并将焦点切换到您创建新项目的原始终端。
注册、构建和部署应用程序
在您连接到在您的开发环境中本地运行的 Internet 计算机网络后,您可以在本地注册、构建和部署您的应用程序。
在本地部署应用程序:
-
如果需要,请检查您是否仍在项目的根目录中。
-
通过运行以下命令注册、构建和部署您的应用程序:
dfx deploy
的
dfx deploy
关于它执行的操作命令输出显示信息。
调用已部署容器上的方法
成功部署容器后,您可以模拟最终用户调用容器提供的方法。在本教程中,您将调用一个get
方法来查询计数器的值,一个increment
在每次调用时递增计数器的set
方法,以及一个传递参数以将计数器更新为您指定的任意值的方法。
要在已部署的容器上测试调用方法:
-
运行以下命令以调用该
get
函数,该函数读取currentValue
已部署容器上变量的当前值:dfx canister call my_counter get
该命令将
currentValue
变量的当前值返回为零:(0)
-
运行以下命令以调用
increment
函数将currentValue
已部署容器上的变量值加1:dfx canister call my_counter increment
该命令增加变量的值——改变它的状态——但不返回结果。
-
重新运行以下命令以获取
currentValue
已部署容器上变量的当前值:dfx canister call my_counter get
该命令将
currentValue
变量的更新值返回为 1:(1)
-
运行其他命令来试验调用其他方法和使用不同的值。
例如,尝试类似以下的命令来设置和返回计数器值:
dfx canister call my_counter set '(987)' dfx canister call my_counter get
返回 987 的当前值。
dfx canister call my_counter increment dfx canister call my_counter get
返回 988 的增量值。
在浏览器中测试功能
容器接口描述语言(通常称为 Candid 或更一般地称为 IDL)提供了一种用于指定容器服务签名的通用语言。Candid 为您提供了一种统一的方式来与用不同语言编写或使用不同工具访问的容器进行交互。例如,无论底层程序是原生 Rust、JavaScript 还是 Motoko,Candid 都提供了一致的服务视图。Candid 还支持不同的工具(例如dfx
命令行界面和网络神经系统应用程序)来共享服务的通用描述。
基于 actor 的类型签名,Candid 还提供了一个 Web 界面,允许您调用容器函数进行测试和调试。
使用dfx deploy
ordfx canister install
命令在本地部署项目后,您可以在浏览器中访问 Candid Web 界面端点。这个 Web 界面(Candid UI)以一种形式公开服务描述,使您能够快速查看和测试功能并尝试输入不同的数据类型,而无需编写任何前端代码。
要使用 Candid Web 界面来测试容器功能:
-
使用
dfx canister id __Candid_UI
命令查找与当前项目关联的 Candid UI 容器标识符。dfx canister id __Candid_UI
该命令显示 Candid UI 的容器标识符,输出类似于以下内容:
r7inp-6aaaa-aaaaa-aaabq-cai
-
复制 Candid UI 容器标识符,以便它在剪贴板中可用。
-
如果您已停止 Internet 计算机,请通过运行以下命令在本地重新启动它:
dfx start --background
-
打开浏览器并导航到
dfx.json
配置文件中指定的地址和端口号。默认情况下,
local
网络绑定到127.0.0.1:8000
地址和端口号。 -
canisterId
将dfx canister id
命令返回的必需参数和 Candid UI 容器标识符添加到 URL。例如,完整的 URL 应该类似于以下内容,但带有
CANDID-UI-CANISTER-IDENTIFIER
由dfx canister id
命令返回的:http://127.0.0.1:8000/?canisterId=<CANDID-UI-CANISTER-IDENTIFIER>
浏览器会显示一个表单,供您指定容器标识符或选择 Candid 描述 (
.did
) 文件。如果您不确定要使用哪个容器标识符,您可以运行该
dfx canister id
命令来查找特定容器名称的标识符。 -
在提供容器 ID字段中为您的应用程序指定容器标识符或描述文件,然后单击转到以显示服务描述。
-
查看程序中定义的函数调用和类型列表。
-
为函数键入适当类型的值,或单击“**随机”**生成值,然后单击“**调用”**或“**查询”**以查看结果。
请注意,根据数据类型,Candid 界面可能会显示用于测试功能的其他配置设置。例如,如果函数接受一个数组,您可能需要在输入值之前指定数组中的项数。
停止本地网络
在您完成程序的试验后,您可以停止本地 Internet 计算机网络,使其不会继续在后台运行。
要停止本地网络:
-
在显示网络操作的终端中,按 Control-C 可中断本地网络进程。
-
通过运行以下命令停止 Internet 计算机网络:
dfx stop
在计算器函数中使用整数
在本教程中,您将编写一个简单的计算器程序,该程序创建一个带有多个公共入口点函数的单个 actor,以执行基本的算术运算。
在本教程中,角色名为Calc
。该程序使用该cell
变量包含一个整数,表示计算器操作的当前结果。
该程序支持以下函数调用:
- 的
add
函数调用接受输入和执行加法。 - 的
sub
函数调用接受输入并执行减法。 - 的
mul
函数调用接受输入,并执行乘法。 - 的
div
函数调用接受输入和执行除法。 - 该
clearall
函数清除cell
作为先前操作结果存储的值,将该cell
值重置为零。
该div
函数还包括防止程序试图除以零的代码。
在你开始之前
在开始本教程之前,请验证以下内容:
- 您已经下载并安装了 DFINITY Canister SDK 包
- 您已停止在本地计算机上运行的任何 Internet 计算机网络进程。
完成本教程大约需要 20 分钟。
创建一个新项目
要为本教程创建一个新项目:
-
如果您还没有打开,请在本地计算机上打开一个终端外壳。
-
如果您正在使用 Internet 计算机项目,请切换到您用于 Internet 计算机项目的文件夹。
-
通过运行以下命令创建一个新项目:
dfx new calc
-
通过运行以下命令切换到您的项目目录:
cd calc
修改默认配置
在本教程中,让我们修改默认dfx.json
配置文件,为其主程序使用更具体的名称。
修改默认dfx.json
配置文件:
-
dfx.json
在文本编辑器中打开配置文件。 -
将
main
密钥设置从默认main.mo
程序名称更改为calc_main.mo
。例如:
"main": "src/calc/calc_main.mo"
对于本教程,将源文件的名称从
main.mo
更改为calc_main.mo
仅说明dfx.json
配置文件中的设置如何确定要编译的源文件。在更复杂的应用程序中,您可能有多个源文件而不是单个
main
程序文件。更复杂的应用程序可能还具有多个源文件之间的特定依赖项,您需要使用dfx.json
配置文件中的设置来管理这些源文件。在这样的场景中——在你的dfx.json
文件中定义了多个容器和程序——将多个文件全部命名main.mo
可能会使你的工作区导航变得更加困难。您为每个程序选择的名称并不重要,但重要的是您在dfx.json
文件中设置的名称与文件系统中程序的名称相匹配。 -
保存更改并关闭文件以继续。
修改默认程序
对于本教程,您需要使用执行基本算术运算的程序替换默认程序。
替换默认程序:
-
如果需要,请检查您是否仍在项目目录中。
-
复制模板
main.mo
文件以创建一个calc_main.mo
通过运行以下命令命名的新文件:cp src/calc/main.mo src/calc/calc_main.mo
-
src/calc/calc_main.mo
在文本编辑器中打开文件并删除现有内容。 -
将以下示例代码复制并粘贴到
calc_main.mo
文件中:// This single-cell calculator defines one calculator instruction per // public entry point (add, sub, mul, div). // Create a simple Calc actor. actor Calc { var cell : Int = 0; // Define functions to add, subtract, multiply, and divide public func add(n:Int) : async Int { cell += n; cell }; public func sub(n:Int) : async Int { cell -= n; cell }; public func mul(n:Int) : async Int { cell *= n; cell }; public func div(n:Int) : async ?Int { if ( n == 0 ) { return null // null indicates div-by-zero error } else { cell /= n; ?cell } }; // Clear the calculator and reset to zero public func clearall() : async Int { if (cell : Int != 0) cell -= cell; return cell }; };
您可能会注意到此示例代码使用整数 (
Int
) 数据类型,使您可以使用正数或负数。如果您想将此计算器代码中的函数限制为仅使用正数,您可以将数据类型更改为仅允许自然 (Nat
) 数据。 -
保存更改并关闭文件以继续。
启动本地网络
在构建calc
项目之前,您需要连接到 Internet 计算机网络,要么在您的开发环境中本地运行,要么在您可以访问的子网上远程运行。
在本地启动网络需要一个dfx.json
文件,因此您应该确保您位于项目的根目录中。对于本教程,您应该有两个独立的终端外壳,以便您可以在一个终端中启动和查看网络操作,并在另一个终端中管理您的项目。
在本地启动网络:
-
在本地计算机上打开一个新的终端窗口或选项卡。
-
如有必要,导航到项目的根目录。
- 您现在应该打开了两个终端。
- 您应该将项目目录作为您当前的工作目录。
-
通过运行以下命令在本地计算机上启动 Internet 计算机网络:
dfx start
启动本地网络后,终端会显示有关网络操作的消息。
-
让显示网络操作的终端保持打开状态,并将焦点切换到您创建新项目的原始终端。
注册、构建和部署应用程序
在您连接到在您的开发环境中本地运行的 Internet 计算机网络后,您可以在本地注册、构建和部署您的应用程序。
在本地部署应用程序:
-
如果需要,请检查您是否仍在项目的根目录中。
-
通过运行以下命令注册、构建和部署您的应用程序:
dfx deploy
的
dfx deploy
关于它执行的操作命令输出显示信息。
验证容器上的计算器功能
您现在已将程序部署为本地 Internet 计算机网络上的容器。您可以使用dfx canister call
命令来测试程序。
要测试您已部署的程序:
-
使用该
dfx canister call
命令调用calc
容器add
函数并通过10
运行以下命令将输入参数传递给它:dfx canister call calc add '(10)'
当您传递由单引号和括号括起来的参数时,接口描述语言 (IDL) 会解析参数类型,因此您无需手动指定参数类型。
验证命令返回
add
函数的预期值。例如,程序显示类似于以下内容的输出:(10)
-
通过运行以下命令调用该
mul
函数并将输入参数传递给它3
:dfx canister call calc mul '(3)'
验证命令返回
mul
函数的预期值。例如,程序显示类似于以下内容的输出:(30)
-
通过运行以下命令调用该
sub
函数并将5
type的输入参数传递给它number
:dfx canister call calc sub '(5)'
验证命令返回
sub
函数的预期值。例如,程序显示类似于以下内容的输出:(25)
-
通过运行以下命令调用该
div
函数并将输入参数传递给它5
:dfx canister call calc div '(5)'
验证命令返回
div
函数的预期值。例如,程序显示类似于以下内容的输出:(选项 5)
您可能会注意到该
div
函数返回一个可选结果。程序将结果设为可选,以便div
函数null
在出现被零除错误的情况下返回。因为这个程序中的单元格变量是一个整数,你也可以调用它的函数并指定负输入值。例如,您可以运行以下命令:
dfx canister call calc mul '(-4)'复制
返回:
(-20)
-
调用该
clearall
函数并验证它是否将cell
值重置为零:dfx canister call calc clearall
例如,程序显示类似于以下内容的输出:
(0)
在浏览器中测试功能
容器接口描述语言(通常称为 Candid 或更一般地称为 IDL)提供了一种用于指定容器服务签名的通用语言。Candid 为您提供了一种统一的方式来与用不同语言编写或使用不同工具访问的容器进行交互。例如,无论底层程序是原生 Rust、JavaScript 还是 Motoko,Candid 都提供了一致的服务视图。Candid 还支持不同的工具(例如dfx
命令行界面和网络神经系统应用程序)来共享服务的通用描述。
基于 actor 的类型签名,Candid 还提供了一个 Web 界面,允许您调用容器函数进行测试和调试。
使用dfx deploy
ordfx canister install
命令在本地部署项目后,您可以在浏览器中访问 Candid Web 界面端点。这个 Web 界面(Candid UI)以一种形式公开服务描述,使您能够快速查看和测试功能并尝试输入不同的数据类型,而无需编写任何前端代码。
要使用 Candid Web 界面来测试容器功能:
-
使用
dfx canister id __Candid_UI
命令查找与当前项目关联的 Candid UI 容器标识符。dfx canister id __Candid_UI
该命令显示 Candid UI 的容器标识符,输出类似于以下内容:
r7inp-6aaaa-aaaaa-aaabq-cai
-
复制 Candid UI 容器标识符,以便它在剪贴板中可用。
-
如果您已停止 Internet 计算机,请通过运行以下命令在本地重新启动它:
dfx start --background
-
打开浏览器并导航到
dfx.json
配置文件中指定的地址和端口号。默认情况下,
local
网络绑定到127.0.0.1:8000
地址和端口号。 -
canisterId
将dfx canister id
命令返回的必需参数和 Candid UI 容器标识符添加到 URL。例如,完整的 URL 应该类似于以下内容,但带有
CANDID-UI-CANISTER-IDENTIFIER
由dfx canister id
命令返回的:http://127.0.0.1:8000/?canisterId=<CANDID-UI-CANISTER-IDENTIFIER>
浏览器会显示一个表单,供您指定容器标识符或选择 Candid 描述 (
.did
) 文件。如果您不确定要使用哪个容器标识符,您可以运行该
dfx canister id
命令来查找特定容器名称的标识符。 -
在提供容器 ID字段中为您的应用程序指定容器标识符或描述文件,然后单击转到以显示服务描述。
-
查看程序中定义的函数调用和类型列表。
-
为函数键入适当类型的值,或单击“**随机”**生成值,然后单击“**调用”**或“**查询”**以查看结果。
请注意,根据数据类型,Candid 界面可能会显示用于测试功能的其他配置设置。例如,如果函数接受一个数组,您可能需要在输入值之前指定数组中的项数。
停止本地网络
在您完成程序的试验后,您可以停止本地 Internet 计算机网络,使其不会继续在后台运行。
要停止本地网络:
-
在显示网络操作的终端中,按 Control-C 可中断本地网络进程。
-
通过运行以下命令停止 Internet 计算机网络:
dfx stop
导入库模块
在本教程中,您将编写一个简单的程序,使您能够存储和查找电话号码。本教程说明了如何导入和使用一些基本的 Motoko 库函数。
在本教程中,Motoko 基本库函数在List
和AssocList
模块中定义 ,使您能够将列表作为链接的键值对进行处理。在此示例中,键是 a name
,值是phone
与该名称关联的文本字符串。
该程序支持以下函数调用:
- 该
insert
函数接受name
和phone
键值对作为存储在book
变量中的输入。 - 该
lookup
函数是一个查询,它使用指定的name
键作为输入来查找相关联的电话号码。
在你开始之前
在开始本教程之前,请验证以下内容:
- 您已经下载并安装了 DFINITY Canister SDK 包
- 您已停止在本地计算机上运行的任何 Internet 计算机网络进程。
完成本教程大约需要 10 分钟。
创建一个新项目
要为本教程创建一个新项目:
-
如果您还没有打开,请在本地计算机上打开一个终端外壳。
-
如果您正在使用 Internet 计算机项目,请切换到您用于 Internet 计算机项目的文件夹。
-
通过运行以下命令创建一个新项目:
dfx new phonebook
-
通过运行以下命令切换到您的项目目录:
cd phonebook
修改默认程序
在本教程中,让我们main.mo
为简单的电话号码查找程序创建一个新文件。
修改默认模板:
-
src/phonebook/main.mo
在文本编辑器中打开文件并删除现有内容。 -
将以下示例代码复制并粘贴到
main.mo
文件中:// Import standard library functions for lists import L "mo:base/List"; import A "mo:base/AssocList"; // The PhoneBook actor. actor { // Type aliases make the rest of the code easier to read. public type Name = Text; public type Phone = Text; // The actor maps names to phone numbers. flexible var book: A.AssocList<Name, Phone> = L.nil<(Name, Phone)>(); // An auxiliary function checks whether two names are equal. func nameEq(l: Name, r: Name): Bool { return l == r; }; // A shared invokable function that inserts a new entry // into the phone book or replaces the previous one. public func insert(name: Name, phone: Phone): async () { let (newBook, _) = A.replace<Name, Phone>(book, name, nameEq, ?phone); book := newBook; }; // A shared read-only query function that returns the (optional) // phone number corresponding to the person with the given name. public query func lookup(name: Name): async ?Phone { return A.find<Name, Phone>(book, name, nameEq); }; };
在查看此示例程序时,您可能会注意到以下关键元素:
- 代码定义
Name
并Phone
作为自定义文本类型。创建用户定义的类型可以提高代码的可读性。 - 该
insert
函数是一个更新调用,该lookup
函数是一个查询调用。 Phone
使用?Phone
语法将该类型标识为可选值。
- 代码定义
启动本地网络
在构建phonebook
项目之前,您需要连接到 Internet 计算机网络,要么在您的开发环境中本地运行,要么在您可以访问的子网上远程运行。
在本地启动网络需要一个dfx.json
文件,因此您应该确保您位于项目的根目录中。对于本教程,您应该有两个独立的终端外壳,以便您可以在一个终端中启动和查看网络操作,并在另一个终端中管理您的项目。
在本地启动网络:
-
在本地计算机上打开一个新的终端窗口或选项卡。
-
如有必要,导航到项目的根目录。
- 您现在应该打开了两个终端。
- 您应该将项目目录作为您当前的工作目录。
-
通过运行以下命令在本地计算机上启动 Internet 计算机网络:
dfx start --clean
在本教程中,我们使用该
--clean
选项以干净的状态启动 Internet 计算机网络。此选项会删除任何可能中断正常操作的孤立后台进程或容器标识符。例如,如果您
dfx stop
在项目之间移动时忘记发出 a ,则可能有一个进程在后台或另一个终端中运行。该--clean
选项确保您可以启动 Internet 计算机网络并继续下一步,而无需手动查找和终止任何正在运行的进程。 -
让显示网络操作的终端保持打开状态,并将焦点切换到您创建新项目的原始终端。
注册、构建和部署应用程序
在您连接到在您的开发环境中本地运行的 Internet 计算机网络后,您可以在本地注册、构建和部署您的应用程序。
在本地部署应用程序:
-
如果需要,请检查您是否仍在项目的根目录中。
-
通过运行以下命令注册、构建和部署您的应用程序:
dfx deploy phonebook
该
dfx.json
文件提供用于创建应用程序前端入口点和assets
容器的默认设置。在之前的教程中,我们删除了资产容器的条目,因为我们没有为示例应用程序添加前端。通过消除未使用的文件,该更改使我们的项目工作区保持整洁。但是,没有要求这样做,并且将资产容器描述留在
dfx.json
文件中也没有坏处。例如,如果您打算稍后添加前端资产,您可能希望将其用作占位符。对于本教程,您可以使用该
dfx deploy phonebook
命令仅部署电话簿后端容器,因为该项目是基于终端的应用程序,不包含任何前端资产。加名称和数字
您现在已将程序部署为本地副本网络上的容器,并且可以使用dfx canister call
命令测试您的程序。
要测试您在本地副本网络上部署的程序:
-
使用该
dfx canister call
命令phonebook
使用该insert
函数调用容器,并通过运行以下命令将名称和电话号码传递给它:dfx canister call phonebook insert '("Chris Lynn", "01 415 792 1333")'
-
通过运行以下命令添加第二个名称和号码对:
dfx canister call phonebook insert '("Maya Garcia", "01 408 395 7276")'
-
lookup
通过运行以下命令,验证该命令是否使用该函数返回了与“Chris Lynn”关联的号码:dfx canister call phonebook lookup '("Chris Lynn")'
该命令返回类似于以下内容的输出:
(opt "01 415 792 1333")
-
lookup
通过运行以下命令,尝试使用与“Maya Garcia”关联的号码调用该函数:dfx canister call phonebook lookup '("01 408 395 7276")'
请注意,在这种情况下,该命令会返回,
(null)
因为电话号码不是与“Maya Garcia”名称条目关联的键。 -
尝试
lookup
通过运行以下命令再次调用该函数以返回“Maya Garcia”和“Chris Lynn”的电话号码:dfx canister call phonebook lookup '("Maya Garcia","Chris Lynn")'
因为该程序被编写为为一个键返回一个值,所以该命令仅返回与第一个键相关联的信息,在本例中为 的电话号码
Maya Garcia
。
在浏览器中测试功能
容器接口描述语言(通常称为 Candid 或更一般地称为 IDL)提供了一种用于指定容器服务签名的通用语言。Candid 为您提供了一种统一的方式来与用不同语言编写或使用不同工具访问的容器进行交互。例如,无论底层程序是原生 Rust、JavaScript 还是 Motoko,Candid 都提供了一致的服务视图。Candid 还支持不同的工具(例如dfx
命令行界面和网络神经系统应用程序)来共享服务的通用描述。
基于 actor 的类型签名,Candid 还提供了一个 Web 界面,允许您调用容器函数进行测试和调试。
使用dfx deploy
ordfx canister install
命令在本地部署项目后,您可以在浏览器中访问 Candid Web 界面端点。这个 Web 界面(Candid UI)以一种形式公开服务描述,使您能够快速查看和测试功能并尝试输入不同的数据类型,而无需编写任何前端代码。
要使用 Candid Web 界面来测试容器功能:
-
使用
dfx canister id __Candid_UI
命令查找与当前项目关联的 Candid UI 容器标识符。dfx canister id __Candid_UI
该命令显示 Candid UI 的容器标识符,输出类似于以下内容:
r7inp-6aaaa-aaaaa-aaabq-cai
-
复制 Candid UI 容器标识符,以便它在剪贴板中可用。
-
如果您已停止 Internet 计算机,请通过运行以下命令在本地重新启动它:
dfx start --background
-
打开浏览器并导航到
dfx.json
配置文件中指定的地址和端口号。默认情况下,
local
网络绑定到127.0.0.1:8000
地址和端口号。 -
canisterId
将dfx canister id
命令返回的必需参数和 Candid UI 容器标识符添加到 URL。例如,完整的 URL 应该类似于以下内容,但带有
CANDID-UI-CANISTER-IDENTIFIER
由dfx canister id
命令返回的:http://127.0.0.1:8000/?canisterId=<CANDID-UI-CANISTER-IDENTIFIER>
浏览器会显示一个表单,供您指定容器标识符或选择 Candid 描述 (
.did
) 文件。如果您不确定要使用哪个容器标识符,您可以运行该
dfx canister id
命令来查找特定容器名称的标识符。 -
在提供容器 ID字段中为您的应用程序指定容器标识符或描述文件,然后单击转到以显示服务描述。
-
查看程序中定义的函数调用和类型列表。
-
为函数键入适当类型的值,或单击“**随机”**生成值,然后单击“**调用”**或“**查询”**以查看结果。
请注意,根据数据类型,Candid 界面可能会显示用于测试功能的其他配置设置。例如,如果函数接受一个数组,您可能需要在输入值之前指定数组中的项数。
修改程序中的源代码
为了扩展您在本教程中学到的知识,您可能想要尝试修改源代码以返回不同的结果。
例如,您可能希望更改源代码,而不是插入和查找当前键值(姓名-电话)对的程序来创建一个程序,该程序存储类似于数据库“记录”的联系信息,其中一个主键与多个字段相关联。在此示例中,您的程序可能允许用户或其他程序添加信息(例如家庭电话号码、手机号码、电子邮件地址和街道地址),并有选择地返回所有或特定字段值。
停止本地网络
在您完成程序的试验后,您可以停止本地 Internet 计算机网络,使其不会继续在后台运行。
要停止本地网络:
-
在显示网络操作的终端中,按 Control-C 可中断本地网络进程。
-
通过运行以下命令停止 Internet 计算机网络:
dfx stop
使用多个actor
在本教程中,您将创建一个包含多个角色的项目。目前,您只能在 Motoko 文件中定义一个 actor,并且单个 actor 始终编译为单个容器。但是,您可以创建具有多个角色的项目,并且可以从同一dfx.json
配置文件构建多个容器。
在本教程中,您将为同一项目中的三个演员创建单独的程序文件。该项目定义了以下不相关的参与者:
-
该
assistant
演员提供函数来在待办事项列表中添加和演出任务。为简单起见,本教程的代码示例仅包含添加待办事项和显示当前已添加待办事项列表的功能。这个程序的一个更完整的版本 - 带有用于将项目标记为完整和从列表中删除项目的附加功能 - 在示例存储库中作为Simple to-do checklist 提供。
-
该
rock_paper_scissors
演员提供了一个硬编码的石头,剪子,布的比赛确定一个胜利者的功能。此代码示例演示了基本的使用
switch
和case
与硬编码的球员和选择一个元子程序。 -
该
daemon
演员提供模拟功能,启动和停止守护程序。此代码示例只是为演示目的分配一个变量并打印消息
在你开始之前
在开始本教程之前,请验证以下内容:
- 您已经下载并安装了 DFINITY Canister SDK 包
- 您已停止在本地计算机上运行的任何 Internet 计算机网络进程。
完成本教程大约需要 20 分钟。
创建一个新项目
要为本教程创建一个新项目:
-
如果您还没有打开,请在本地计算机上打开一个终端外壳。
-
如果您正在使用 Internet 计算机项目,请切换到您用于 Internet 计算机项目的文件夹。
-
通过运行以下命令创建一个新项目:
dfx new multiple_actors
-
通过运行以下命令切换到您的项目目录:
cd multiple_actors
修改默认配置
你已经看到,创建一个新的项目增加了一个默认dfx.json
的配置文件到您的项目目录。对于本教程,您需要向此文件添加部分以指定定义要构建的演员的每个程序的位置。
修改默认dfx.json
配置文件:
-
dfx.json
在文本编辑器中打开配置文件,然后将默认multiple_actors
容器名称和源目录更改为assistant
.例如,在
canisters
键下:"assistant": { "main": "src/assistant/main.mo", "type": "motoko" }
因为您要向
canisters
配置文件的这一部分添加设置,您还必须在包含主源代码文件位置和容器类型的花括号后添加一个逗号assistant
。 -
multiple_actors_assets
从文件中删除该部分。 -
为程序添加新的容器名称、源代码位置和容器类型
rock_paper_scissors
,并为容器定义daemon
下方的程序文件添加新的容器名称、源代码位置和容器类型assistant
。进行更改后,文件
canisters
部分dfx.json
应类似于以下内容:{ "canisters": { "assistant": { "main": "src/assistant/main.mo", "type": "motoko" }, "rock_paper_scissors": { "main": "src/rock_paper_scissors/main.mo", "type": "motoko" }, "daemon": { "main": "src/daemon/main.mo", "type": "motoko" } }, "defaults": { "build": { "packtool": "" } }, "dfx": "0.7.2", "networks": { "local": { "bind": "127.0.0.1:8000", "type": "ephemeral" } }, "version": 1 }
您可以按原样保留其他部分。
-
保存更改并关闭
dfx.json
文件以继续。 -
dfx.json
通过运行以下命令,更改默认源文件目录的名称以匹配配置文件中指定的名称:cp -r src/multiple_actors/ src/assistant/
-
通过运行以下命令复制
assistant
源文件目录以创建rock_paper_scissors
actor的主程序文件:cp -r src/assistant/ src/rock_paper_scissors/
-
通过运行以下命令复制
assistant
源文件目录以创建daemon
actor的主程序文件:cp -r src/assistant/ src/daemon/
修改默认程序
您现在在目录中有三个独立的src
目录,每个目录都有一个模板main.mo
文件。在本教程中,您将main.mo
使用不同的参与者替换每个模板文件中的内容。
修改默认源代码:
-
src/assistant/main.mo
在文本编辑器中打开文件并删除现有内容。 -
将以下示例代码复制并粘贴到文件中:
import Array "mo:base/Array"; import Nat "mo:base/Nat"; // Define the actor actor Assistant { stable var todos : [ToDo] = []; stable var nextId : Nat = 1; // Define to-do item properties type ToDo = { id : Nat; description : Text; completed : Bool; }; // Add to-do item utility func add(todos : [ToDo], description : Text, id : Nat) : [ToDo] { let todo : ToDo = { id = id; description = description; completed = false; }; Array.append(todos, [todo]) }; // Show to-do item utility func show(todos : [ToDo]) : Text { var output : Text = "\n___TO-DOs___"; for (todo : ToDo in todos.vals()) { output #= "\n(" # Nat.toText(todo.id) # ") " # todo.description; if (todo.completed) { output #= " ✔"; }; }; output }; public func addTodo (description : Text) : async () { todos := add(todos, description, nextId); nextId += 1; }; public query func showTodos () : async Text { show(todos) }; };
-
保存更改并关闭
main.mo
文件以继续。 -
src/rock_paper_scissors/main.mo
在文本编辑器中打开文件并删除现有内容。 -
将以下示例代码复制并粘贴到文件中:
import I "mo:base/Iter"; actor RockPaperScissors { stable var alice_score : Nat = 0; stable var bob_score : Nat = 0; stable var alice_last : Choice = #scissors; stable var bob_last : Choice = #rock; type Choice = { #rock; #paper; #scissors; }; public func contest() : async Text { for (i in I.range(0, 99)) { battle_round(); }; var winner = "The contest was a draw"; if (alice_score > bob_score) winner := "Alice won" else if (alice_score < bob_score) winner := "Bob won"; return (winner); }; func battle_round() : () { let a = alice(bob_last); let b = bob(alice_last); switch (a, b) { case (#rock, #scissors) alice_score += 1; case (#rock, #paper) bob_score += 1; case (#paper, #scissors) alice_score += 1; case (#paper, #rock) bob_score += 1; case (#scissors, #paper) alice_score += 1; case (#scissors, #rock) bob_score += 1; case (#rock, #rock) alice_score += 0; case (#paper, #paper) bob_score += 0; case (#scissors, #scissors) alice_score += 0; }; alice_last := a; bob_last := b; return (); }; // Hard-coded players and choices func bob(last : Choice) : Choice { return #paper; }; func alice(last : Choice) : Choice { return #rock; }; };
-
保存更改并关闭
main.mo
文件以继续。 -
src/daemon/main.mo
在文本编辑器中打开文件并删除现有内容。 -
将以下示例代码复制并粘贴到文件中:
actor Daemon { stable var running = false; public func launch() : async Text { running := true; debug_show "The daemon process is running"; }; public func stop(): async Text { running := false; debug_show "The daemon is stopped"; }; };
-
保存更改并关闭
main.mo
文件以继续。
启动本地网络
在构建multiple_actors
项目之前,您需要连接到 Internet 计算机网络,要么在您的开发环境中本地运行,要么在您可以访问的子网上远程运行。
在本地启动网络:
-
在本地计算机上打开一个新的终端窗口或选项卡。
-
如有必要,导航到项目的根目录。
-
通过运行以下命令在本地计算机上启动 Internet 计算机网络:
dfx start
-
让显示网络操作的终端保持打开状态,并将焦点切换到您创建新项目的原始终端。
注册、构建和部署您的应用程序
连接到在开发环境中本地运行的 Internet 计算机网络后,您可以在本地注册、构建和部署多容器应用程序。
在本地部署应用程序:
-
如果需要,请检查您是否仍在项目的根目录中。
-
通过运行以下命令注册、构建和部署应用程序:
dfx deploy
如果您有权访问远程运行的 Internet 计算机,则可以通过指定
--network
选项和dfx.json
文件中配置的网络别名来部署到该网络,而不是在本地部署。例如,如果您要连接到由网络别名指定的 URL,ic
您将运行类似以下的命令:dfx deploy --network ic
的
dfx deploy
关于它执行的操作命令输出显示信息。例如,该命令显示dfx.json
配置文件中定义的三个容器的特定于网络的容器标识符。Deploying all canisters. Creating canisters... Creating canister "assistant"... "assistant" canister created with canister id: "75hes-oqbaa-aaaaa-aaaaa-aaaaa-aaaaa-aaaaa-q" Creating canister "daemon"... "daemon" canister created with canister id: "cxeji-wacaa-aaaaa-aaaaa-aaaaa-aaaaa-aaaaa-q" Creating canister "rock_paper_scissors"... "rock_paper_scissors" canister created with canister id: "7kncf-oidaa-aaaaa-aaaaa-aaaaa-aaaaa-aaaaa-q"
通过调用函数验证部署
您现在已将三个程序部署为本地副本网络上的容器,并且可以使用dfx canister call
命令测试每个程序。
要测试您已部署的程序:
-
使用该
dfx canister call
命令assistant
使用该addTodo
函数调用容器,并通过运行以下命令将要添加的任务传递给它:dfx canister call assistant addTodo '("Schedule monthly demos")'
-
showTodos
通过运行以下命令,验证该命令是否使用该函数返回了待办事项列表项:dfx canister call assistant showTodos
该命令返回类似于以下内容的输出:
(" ___TO-DOs___ (1) Schedule monthly demos")
-
通过运行以下命令,使用该
dfx canister call
命令调用rock_paper_scissors
使用该contest
函数的容器:dfx canister call rock_paper_scissors contest
该命令返回类似于以下内容的硬编码竞赛的结果:
("Bob won")
-
通过运行以下命令,使用该
dfx canister call
命令调用daemon
使用该launch
函数的容器:dfx canister call daemon launch
-
验证模拟
launch
函数返回“守护进程正在运行”消息”:(""The daemon process is running"")
停止本地网络
在您完成程序的试验后,您可以停止本地 Internet 计算机网络,使其不会继续在后台运行。
要停止本地网络:
-
在显示网络操作的终端中,按 Control-C 可中断本地网络进程。
-
通过运行以下命令停止 Internet 计算机网络:
dfx stop
自定义前端
既然您对如何创建、构建和部署简单程序有了基本的了解,并且熟悉默认项目文件和示例前端,您可能想要开始尝试不同的方式来自定义前端用户体验为您的项目。
本教程演示了如何使用简单的 React 框架为默认示例程序创建新的前端,并指导您完成一些基本修改以自定义显示的界面。后面的教程会扩展这里介绍的技术,但如果您已经知道如何使用 CSS、HTML、JavaScript 和 React 或其他框架来构建您的用户界面,则可以跳过本教程。
本教程说明了如何使用 React 框架来管理容器的文档对象模型 (DOM)。因为 React 有自己自定义的 DOM 语法,所以需要修改 webpack 配置来编译前端代码,前端代码是用 JSX 编写的。有关学习使用 React 和 JSX 的更多信息,请参阅React 网站上的入门。
在你开始之前
在开始本教程之前,请验证以下内容:
- 您已
node.js
安装用于前端开发,并且可以安装npm install
在您的项目中使用的包。有关为本地操作系统和包管理器安装 node 的信息,请参阅Node网站。 - 您已经下载并安装了 DFINITY Canister SDK 包
- 如果您使用 Visual Studio Code 作为 IDE,您已经为 Motoko 安装了 Visual Studio Code 插件。
- 您已停止在本地计算机上运行的任何 Internet 计算机网络进程。
完成本教程大约需要 30 分钟。
创建一个新项目
为您的自定义前端应用程序创建一个新的项目目录:
-
如果您还没有打开,请在本地计算机上打开一个终端外壳。
-
如果您正在使用 Internet 计算机项目,请切换到您用于 Internet 计算机项目的文件夹。
-
node.js
通过运行以下命令检查您是否已在本地安装:which node which npm
如果您尚未
node.js
安装,则应先下载并安装它,然后再继续下一步。有关为本地操作系统和包管理器安装 node 的信息,请参阅Node网站。 -
通过运行以下命令创建一个新项目:
dfx new custom_greeting
该
dfx new custom_greeting
命令创建一个新custom_greeting
项目。 -
通过运行以下命令切换到您的项目目录:
cd custom_greeting
安装 React 框架
如果您以前从未使用过 React,那么在编辑前端代码之前,您可能想浏览React教程或React 网站。
要安装所需的框架模块:
-
通过运行以下命令安装 React 模块:
npm install --save react react-dom
-
通过运行以下命令安装所需的 TypeScript 语言编译器加载器:
npm install --save typescript ts-loader复制
{ "name": "custom_greeting_assets", "version": "0.1.0", "description": "", "keywords": [], "scripts": { "build": "webpack" }, "devDependencies": { "@dfinity/agent": "0.7.2", "assert": "2.0.0", "buffer": "6.0.3", "events": "3.3.0", "html-webpack-plugin": "5.3.1", "process": "0.11.10", "stream-browserify": "3.0.0", "terser-webpack-plugin": "5.1.1", "util": "0.12.3", "webpack": "5.24.4", "webpack-cli": "4.5.0" }, "dependencies": { "react": "^17.0.2", "react-dom": "^17.0.2", "ts-loader": "^8.1.0", "typescript": "^4.2.4" } }
查看默认配置
在我们对本教程使用 React 进行任何更改之前,让我们回顾一下dfx.json
您项目的配置文件中的默认前端设置。
查看默认dfx.json
配置文件:
-
dfx.json
在文本编辑器中打开配置文件。 -
请注意,该
canisters
键包含custom_greeting_assets
容器的设置。{ "canisters": { ... "custom_greeting_assets": { "dependencies": [ "custom_greeting" ], "frontend": { "entrypoint": "src/custom_greeting_assets/src/index.html" }, "source": [ "src/custom_greeting_assets/assets", "dist/custom_greeting_assets/" ], "type": "assets" } } }
我们来看看本节中的设置。
- 项目的前端资产被编译到它们自己的容器中,在本例中,名为
custom_greeting_assets
. - 资产容器默认依赖于项目的主容器。
- 该
frontend.entrypoint
设置指定index.html
要用作应用程序入口点的文件(在本例中为文件)的路径。如果您有不同的起点(例如,自定义first-page.html
文件),您将修改此设置。 - 这些
source
设置指定了您的src
和dist
目录的路径。该src
设置指定用于在构建项目时包含在资产容器中的静态资产的目录。如果您有自定义级联样式表 (CSS) 或 JavaScript 文件,您可以将它们包含在此路径指定的文件夹中。构建项目后,项目资产将从dist
设置指定的目录中提供。 - 该
type
设置指定custom_greeting_assets
是资产容器而不是程序容器。
在本教程中,我们将在
index.jsx
文件中添加 React JavaScript ,但这不需要对dfx.json
文件中的默认设置进行任何更改。 - 项目的前端资产被编译到它们自己的容器中,在本例中,名为
-
关闭
dfx.json
文件以继续。
查看默认的前端文件
对于本教程,您将使用默认main.mo
程序并且仅通过修改前端来操作应用程序。不过,在进行任何更改之前,让我们先看看项目的默认前端文件中的内容。
查看默认的前端文件:
-
src/custom_greeting_assets/src/index.html
在文本编辑器中打开文件。此模板文件是应用程序的默认前端入口点,由文件中的
frontend.entrypoint
设置指定dfx.json
。此文件包含标准 HTML,其中包含对 CSS 文件和位于
src/custom_greeting_assets/assets
目录中的图像的引用。默认index.html
文件还包括用于显示name
参数的输入字段和可点击按钮的标准 HTML 语法。 -
src/custom_greeting_assets/src/index.js
在文本编辑器中打开文件。import { Actor, HttpAgent } from '@dfinity/agent'; import { idlFactory as custom_greeting_idl, canisterId as custom_greeting_id } from 'dfx-generated/custom_greeting'; const agent = new HttpAgent(); const custom_greeting = Actor.createActor(custom_greeting_idl, { agent, canisterId: custom_greeting_id }); document.getElementById("clickMeBtn").addEventListener("click", async () => { const name = document.getElementById("name").value.toString(); const greeting = await custom_greeting.greet(name); document.getElementById("greeting").innerText = greeting; });
import
默认index.js
文件中的第一条语句使用 JavaScript 代理库创建一个参与者和一个代理实例。- 第二个
import
语句为custom_greeting
要构造的actor 对象准备容器。 - 接下来的两个语句构造代理和参与者对象。
- 其余行为默认应用程序提供文档对象处理。
-
关闭
index.js
文件以继续。
修改前端文件
您现在已准备好为默认程序创建新的前端。
准备前端文件:
-
webpack.config.js
在文本编辑器中打开 webpack 配置文件 ( )。 -
修改前端进入替换默认
index.html
使用index.jsx
。entry: { // The frontend.entrypoint points to the HTML file for this build, so we need // to replace the extension to `.js`. index: path.join(__dirname, info.frontend.entrypoint).replace(/\.html$/, ".jsx"), },
-
在
module
该plugins
部分上方添加以下键:module: { rules: [ { test: /\.(js|ts)x?$/, loader: "ts-loader" } ] },
此设置使程序能够将
ts-loader
编译器用于 React JavaScriptindex.jsx
文件。请注意,默认webpack.config.js
文件中有一个注释部分,您可以修改该部分以添加module
密钥。 -
tsconfig.json
在您的项目的根目录中创建一个名为的新文件。 -
tsconfig.json
在文本编辑器中打开文件,然后将以下内容复制并粘贴到文件中:{ "compilerOptions": { "target": "es2018", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ "lib": ["ES2018", "DOM"], /* Specify library files to be included in the compilation. */ "allowJs": true, /* Allow javascript files to be compiled. */ "jsx": "react", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ }, "include": ["src/**/*"], }
-
保存更改并关闭
tsconfig.json
文件以继续。 -
src/custom_greeting_assets/src/index.js
在文本编辑器中打开默认文件并删除第 7 到 12 行。 -
将以下示例代码复制并粘贴到
index.js
文件中:// Insert these lines after the import statements for // importing an agent and an actor import * as React from 'react'; import { render } from 'react-dom'; // Replace the default index.js content with // React JavaScript class MyHello extends React.Component { constructor(props) { super(props); this.state = { name: 'Name', message: '', }; } async doGreet() { const greeting = await custom_greeting.greet(this.state.name); this.setState({ ...this.state, message: greeting }); } onNameChange(ev) { this.setState({ ...this.state, name: ev.target.value }); } render() { return ( <div style={{ "font-size": "30px" }}> <div style={{ "background-color": "yellow" }}> <p>Greetings, from DFINITY!</p> <p> Type your message in the Name input field, then click <b> Get Greeting</b> to display the result.</p> </div> <div style={{ "margin": "30px" }}> <input id="name" value={this.state.name} onChange={ev => this.onNameChange(ev)}></input> <button onClick={() => this.doGreet()}>Get Greeting!</button> </div> <div>Greeting is: "<span style={{ "color": "blue" }}>{this.state.message}</span>"</div> </div> ); } } render(<MyHello />, document.getElementById('app'));
-
通过运行以下命令将修改后的
index.js
文件重命名为index.jsx
:mv src/custom_greeting_assets/src/index.js src/custom_greeting_assets/src/index.jsx
-
src/custom_greeting_assets/src/index.html
在文本编辑器中打开默认文件,然后将正文内容替换为<div id="app"></div>
.例如:
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width"> <title>custom_greeting</title> <base href="/"> <link type="text/css" rel="stylesheet" href="main.css" /> </head> <body> <div id="app"></div> </body> </html>
启动本地网络
在构建custom_greeting
项目之前,您需要连接到 Internet 计算机网络,要么在您的开发环境中本地运行,要么在您可以访问的子网上远程运行。
在本地启动网络:
-
在本地计算机上打开一个新的终端窗口或选项卡。
-
如有必要,导航到项目的根目录。
-
通过运行以下命令在本地计算机上启动 Internet 计算机网络:
dfx start --background
在本地 Internet 计算机网络完成其启动操作后,您可以继续下一步。
注册、构建和部署应用程序
在您连接到在您的开发环境中本地运行的 Internet 计算机网络后,您可以在本地注册、构建和部署您的应用程序。
在本地部署应用程序:
-
如果需要,请检查您是否仍在项目的根目录中。
-
通过运行以下命令注册、构建和部署您的应用程序:
dfx deploy复制
的
dfx deploy
关于它执行的操作命令输出显示信息。 -
将容器的容器标识符复制
custom_greeting_assets
到剪贴板或记事本应用程序。如果您没有记下容器标识符,则可以通过运行以下命令来查找它:
dfx canister id custom_greeting_assets
查看新的前端
您现在可以通过在浏览器中输入资产容器的容器标识符来访问默认程序的新前端。
查看自定义前端:
-
打开浏览器并导航到配置文件中
local
指定的网络地址和端口号dfx.json
。例如,如果您使用本地网络的默认绑定,请导航到
127.0.0.1:8000/
。要指定您希望 Web 服务器显示
canisterId
的custom_greeting_assets
容器,请使用以下语法将参数和 容器标识符添加到 URL:?canisterId=<YOUR-ASSET-CANISTER-IDENTIFIER>
例如,完整 URL 应类似于以下内容,但在部署容器时
_canister_identifier_
为custom_greeting_assets
容器返回:http://127.0.0.1:8000/?canisterId=cxeji-wacaa-aaaaa-aaaaa-aaaaa-aaaaa-aaaaa-q
-
确认系统提示您键入问候语。
例如:
-
将输入字段中的Name替换为您要显示的文本,然后单击Get Greeting以查看结果。
例如:
修改前端并测试您的更改
查看前端后,您可能需要进行一些更改。
修改前端:
-
index.jsx
在文本编辑器中打开文件并修改其样式设置。例如,您可能希望通过进行类似于以下的更改来更改字体系列并为输入字段使用占位符:// Modify the front-end in the React JavaScript class MyHello extends React.Component { constructor(props) { super(props); this.state = { name: '', message: '', }; } async doGreet() { const greeting = await custom_greeting.greet(this.state.name); this.setState({ ...this.state, message: greeting }); } onNameChange(ev) { this.setState({ ...this.state, name: ev.target.value }); } render() { return ( <div style={{ "font-family": "sans-serif" }}> <div style={{ "font-size": "30px" }}> <p>Greetings, from DFINITY!</p> <p> Type your message in the Name input field, then click <b> Get Greeting</b> to display the result.</p> <div style={{ "margin": "30px" }}> <input id="name" placeholder="Type text here" value={this.state.name} onChange={ev => this.onNameChange(ev)}></input> <button onClick={() => this.doGreet()}>Get Greeting!</button> </div> <div>Greeting is: "<span style={{ "color": "green" }}>{this.state.message}</span>"</div> </div> </div> ); } } render(<MyHello />, document.getElementById('app'));
-
通过运行以下命令,使用您的更改重新构建项目。
dfx build
-
通过运行以下命令部署您的项目更改:
dfx canister install --all --mode reinstall
-
通过重新加载显示
custom_greeting_assets
容器的页面在浏览器中查看结果。例如:
- 键入一条新消息并查看您的新问候语。例如:
停止本地网络
在您完成程序前端的试验后,您可以停止本地 Internet 计算机网络,使其不会继续在后台运行。
要停止本地网络:
-
在显示网络操作的终端中,按 Control-C 可中断本地网络进程。
-
通过运行以下命令停止 Internet 计算机网络:
dfx stop
添加样式表
级联样式表是为应用程序定制用户体验的最常见方式之一。本教程说明了如何在使用 React 为项目创建新前端时添加样式表。如果您已经知道如何将级联样式表 (CSS) 添加到基于 React 的项目中,则可以跳过本教程。
此处不做过多赘述
进行容器间的调用
对于开发人员而言,Internet 计算机最重要的功能之一是能够从另一个容器中的程序构建、部署和调用一个容器中的共享功能。这种在容器之间进行调用的功能(有时也称为容器间调用)使您能够在多个应用程序中重用和共享功能。
例如,您可能想要创建一个用于专业网络、组织社区活动或举办筹款活动的应用程序。这些应用程序中的每一个都可能有一个社交组件,使用户能够根据某些标准或共同兴趣(例如朋友和家人或现任和前任同事)识别社交关系。
为了解决这个社交组件,您可能想要创建一个用于存储用户关系的容器,然后编写您的专业网络、社区组织或筹款应用程序来导入和调用容器中定义的用于社交关系的函数。然后,您可以构建其他应用程序来使用社交联系容器或扩展社交联系容器提供的功能,使其对更广泛的其他开发人员社区有用。
基于 Motoko 的 LinkedUp 示例应用程序提供了一个开放专业网络的简单实现,演示了如何在项目中使用容器间调用。
LinkedUp 示例应用程序是使用以下容器实现的:
- 该
linkedup
容器创建并存储为用户的基本个人资料,包括工作经历和教育背景。 - 该
connectd
容器创建并存储用户的连接。 - 该
linkedup_assets
容器存储的前端资产,包括JavaScript的,HTML和CSS文件,定义用户界面
在你开始之前
在构建示例应用程序之前,请验证以下内容:
- 您已经下载并安装了 DFINITY Canister SDK 包
- 您已停止在本地计算机上运行的任何 Internet 计算机网络进程。
下载演示
要使用 LinkedUp 示例应用程序试验容器间调用:
-
打开终端外壳并切换到您用于 Internet 计算机示例项目的文件夹。
-
克隆
linkedup
存储库。git clone https://github.com/dfinity/linkedup.git
-
更改到
linkedup
存储库的本地工作目录。cd linkedup
-
通过运行以下命令安装节点模块:
npm install
如有必要,请通过运行以下命令修复发现的任何漏洞:
npm audit fix
-
dfx.json
在文本编辑器中打开文件并验证dfx
设置与dfx
您安装的可执行文件的版本号相同。
启动本地网络
在构建linkedup
项目之前,您需要连接到 Internet 计算机网络,要么在您的开发环境中本地运行,要么在您可以访问的子网上远程运行。
在本地启动网络:
-
在本地计算机上打开一个新的终端窗口或选项卡。
-
如有必要,导航到项目的根目录。
-
通过运行以下命令在本地计算机上启动 Internet 计算机网络:
dfx start --background复制
在本地 Internet 计算机网络完成其启动操作后,您可以继续下一步。
注册容器标识符
在您连接到在您的开发环境中本地运行的 Internet 计算机网络后,您可以向网络注册以生成您项目的唯一容器标识符。
要为本地网络注册容器标识符:
-
如果需要,请检查您是否仍在项目目录中。
-
通过运行以下命令为项目注册唯一的容器标识符:
dfx canister create --all
该命令显示
dfx.json
配置文件中定义的容器的特定于网络的容器标识符。"connectd" canister created with canister id: "75hes-oqbaa-aaaaa-aaaaa-aaaaa-aaaaa-aaaaa-q" "linkedup" canister created with canister id: "cxeji-wacaa-aaaaa-aaaaa-aaaaa-aaaaa-aaaaa-q" "linkedup_assets" canister created with canister id: "7kncf-oidaa-aaaaa-aaaaa-aaaaa-aaaaa-aaaaa-q"
请记住,由于您在本地运行 Internet 计算机,因此这些标识符仅在本地网络上有效。要在远程网络上部署容器,您必须使用
--network
命令行选项和特定网络名称或地址连接到该网络,以在该网络上注册标识符。
构建和部署演示项目
要构建和部署 LinkUp 示例应用程序,请执行以下步骤:
-
pwd
如有必要,请通过运行命令检查您是否仍在项目目录中。 -
通过运行以下命令构建 LinkedUp 容器:
dfx build
-
通过运行以下命令在本地网络上部署项目:
dfx canister install --all
您应该会看到和容器的容器标识符
connectd
,linkedup
并linkedup_assets
带有类似于以下内容的消息:Installing code for canister connectd, with canister_id 75hes-oqbaa-aaaaa-aaaaa-aaaaa-aaaaa-aaaaa-q Installing code for canister linkedup, with canister_id cxeji-wacaa-aaaaa-aaaaa-aaaaa-aaaaa-aaaaa-q Installing code for canister linkedup_assets, with canister_id 7kncf-oidaa-aaaaa-aaaaa-aaaaa-aaaaa-aaaaa-q
-
复制命令
linkedup_assets
返回的容器标识符dfx canister install
。在此示例应用程序中,只有
linkedup_assets
容器包含用于访问应用程序功能的前端资产。因此,要在浏览器中打开应用程序,您需要指定linkedup_assets
容器标识符。 -
linkedup_assets
在 Web 浏览器中打开容器。例如,如果绑定到默认的 localhost 地址和端口号,则 URL 看起来类似于:
http://127.0.0.1:8000/?canisterId=7kncf-oidaa-aaaaa-aaaaa-aaaaa-aaaaa-aaaaa-q
创建配置文件和连接
要运行 LinkedUp 示例应用程序的演示,请执行以下步骤:
-
打开浏览器选项卡或窗口。
-
键入 Web 服务器主机名、端口和
canisterId
关键字,然后将linkedup_assets
容器标识符粘贴为要显示的 URL。127.0.0.1:8000/?canisterId=<ic-identifier-for-linkedup-assets>
浏览器显示一个介绍页面。
将自动生成公私钥对来建立您访问容器的身份,因此在使用该服务之前无需提供用户名和密码或注册帐户来存储您的身份。
-
点击登录。
浏览器显示一个空的个人资料页面。
-
点击编辑,输入个人资料信息,复制并粘贴头像照片的图片地址,然后点击提交。
单击Submit 后,您将拥有一个包含一些可以查看的工作历史记录的个人资料。
例如:
添加另一个配置文件
此时,没有其他配置文件要搜索或添加为连接。要试用搜索和连接功能,您可以:
- 运行一个脚本,用一些额外的配置文件填充示例应用程序。
- 通过打开私人窗口手动创建配置文件。
对于本教程,您将手动创建另一个配置文件。
要添加具有不同身份的用户配置文件:
-
在浏览器窗口的右上角,单击相应的图标以显示浏览器的菜单选项。
例如,如果您使用的是 Google Chrome,则单击垂直椭圆以显示“更多”菜单。
-
如果您使用的是 Google Chrome,请单击“**新建隐身窗口”,如果您使用的是 Firefox,请单击“****新建隐私窗口”,**这样您就可以导航到容器,而无需在与容器的初始浏览器连接中生成用户身份。
-
将第一个浏览器会话中的 URL 复制并粘贴到隐私浏览窗口中,然后点击登录。
请注意,隐私浏览窗口中没有配置文件,但您的原始配置文件在初始浏览器选项卡中仍然可见。
-
点击编辑,输入个人资料信息,复制并粘贴头像照片的图片地址,然后点击提交。
单击Submit 后,您将获得第二个个人资料,其中包含一些可以查看的工作历史记录。
例如:
- 键入您创建的第一个配置文件中的名字或姓氏(例如,如果您为 Maya Garcia 创建了配置文件,请键入 Maya),然后单击“搜索”。
将显示与您的搜索条件匹配的配置文件。
- 从搜索结果中选择联系人,等待显示连接按钮,然后点击连接。
当连接请求完成时,第二个配置文件显示与第一个配置文件的连接。例如:
-
使用您的原始配置文件返回浏览器选项卡。
如果您想在原始配置文件和您在隐私浏览窗口中创建的配置文件之间创建连接,您可以通过重复搜索、选择和连接步骤来实现。
探索配置文件
既然您已经了解了示例应用程序的基本功能,您就可以了解如何使用配置设置和源文件的一些上下文。
要浏览配置文件:
- 切换到
linkedup
目录,然后打开项目的dfx.json
文件。 - 请注意,定义了两个主要容器,
connectd
并且linkedup
每个容器都有一个main.mo
源文件。 - 请注意,
linkedup_assets
容器main.js
以 CSS 和 HTML 文件的形式指定了资产的前端入口点。 - 请注意,应用程序使用默认服务器 IP 地址和端口号。
探索连接的源代码
社交关系容器 的源代码connectd
组织成以下文件:
- 该
digraph.mo
文件提供了创建顶点和边的有向图以描述用户连接的功能。 - 在
main.mo
包含用于定义与能够由LinkedUp示例应用程序被称为用户简档相关联的连接的演员和按键功能。 - 该
types.mo
文件定义了一个顶点映射到用户身份在使用自定义类型digraph
和main
程序文件。
探索链接的源代码
具有工作经历和教育背景的专业简介的源代码组织成以下文件:
- 该
main.mo
文件包含 LinkedUp 示例应用程序的角色和关键函数。 - 该
types.mo
文件定义了自定义类型,这些自定义类型描述了main
在linkedup
容器的程序文件中导入和使用的用户身份和配置文件字段。 - 该
utils.mo
文件提供了帮助函数。
查询和更新操作
在使用 LinkedUp 示例应用程序时,您可能会注意到某些操作(例如查看配置文件或执行搜索)几乎立即返回结果。其他操作(例如创建配置文件或添加连接)需要更长的时间。
这些性能差异说明了在linkedup
容器中使用查询和更新调用之间的差异。
例如,在src/linkedup/main.mo
文件中,create
和update
函数是更改容器状态的更新调用,但程序使用get
和search
函数的查询调用来查看或搜索配置文件:
// Profiles
public shared(msg) func create(profile: NewProfile): async () {
directory.createOne(msg.caller, profile);
};
public shared(msg) func update(profile: Profile): async () {
if(Utils.hasAccess(msg.caller, profile)) {
directory.updateOne(profile.id, profile);
};
};
public query func get(userId: UserId): async Profile {
Utils.getProfile(directory, userId)
};
public query func search(term: Text): async [Profile] {
directory.findBy(term)
};
容器之间的相互作用
在此示例应用程序中,linkedup
容器利用容器中定义的函数connectd
。这种分离简化了每个容器中的代码,并说明了如何通过从一个或多个其他容器调用一个容器中定义的通用函数来扩展项目。
要使一个容器中定义的公共函数在另一个容器中可用:
-
import
在调用容器中添加一条语句。在此示例中,公共函数在
connectd
容器中定义并由linkedup
容器调用。因此,
src/linkedup/main.mo
包括以下代码:// Make the Connectd app's public methods available locally import Connectd "canister:connectd";
跑步
-
使用
canister.function
语法在导入的容器中调用公共方法。在此示例中,
linkedup
容器调用导入容器中的connect
andgetConnections
函数connectd
。
您可以在源文件中看到启用linkedup
容器和connectd
容器之间交互的main.mo
代码。
例如,src/connectd/main.mo
定义了以下函数:
actor Connectd {
flexible var graph: Digraph.Digraph = Digraph.Digraph();
public func healthcheck(): async Bool { true };
public func connect(userA: Vertex, userB: Vertex): async () {
graph.addEdge(userA, userB);
};
public func getConnections(user: Vertex): async [Vertex] {
graph.getAdjacent(user)
};
};
由于该Import
语句,这些connectd
函数可用于linkedup
容器,并且src/linkedup/main.mo
包含以下代码:
// Connections
public shared(msg) func connect(userId: UserId): async () {
// Call Connectd's public methods without an API
await Connectd.connect(msg.caller, userId);
};
public func getConnections(userId: UserId): async [Profile] {
let userIds = await Connectd.getConnections(userId);
directory.findMany(userIds)
};
public shared(msg) func isConnected(userId: UserId): async Bool {
let userIds = await Connectd.getConnections(msg.caller);
Utils.includes(userId, userIds)
};
// User Auth
public shared query(msg) func getOwnId(): async UserId { msg.caller }
};
停止本地网络
在您完成对linkedup
程序的试验后,您可以停止本地 Internet 计算机网络,使其不会继续在后台运行。
要停止本地网络:
-
在显示网络操作的终端中,按 Control-C 可中断本地网络进程。
-
通过运行以下命令停止 Internet 计算机网络:
dfx stop
添加带有身份的访问控制
应用程序通常需要基于角色的权限来控制不同用户可以执行的操作。
为了说明如何创建和切换用户身份,本教程创建了一个简单的程序,为分配到不同角色的用户显示不同的问候语。
在这个例子中,有三个命名角色owner
—— admin
、 和authorized
。
- 分配了
admin
角色的用户会看到显示 的问候语You have a role with administrative privileges
。 - 分配了
authorized
角色的用户会看到显示 的问候语Would you like to play a game?
。 - 未分配这些角色之一的用户会看到显示 的问候语
Nice to meet you!
。
此外,只有初始化容器的用户身份才会被分配owner
角色,只有owner
和admin
角色才能将角色分配给其他用户。
概括地说,每个用户都有一个公钥/私钥对。公钥与用户访问的容器标识符结合形成一个安全主体,然后可以用作消息调用者来验证对运行在 Internet 计算机上的容器的函数调用。下图提供了用户身份如何验证消息调用者的简化视图。
在你开始之前
在开始本教程之前,请验证以下内容:
- 您已经下载并安装了 DFINITY Canister SDK 包
- 您至少运行了一个命令来
default
创建您的用户身份。对于$HOME/.config/dfx/identity/
目录中的所有项目,您的默认用户身份将被全局存储。 - 如果您使用 Visual Studio Code 作为 IDE,您已经为 Motoko 安装了 Visual Studio Code 插件。
- 您已停止在本地计算机上运行的任何 Internet 计算机网络进程。
创建一个新项目
创建一个用于测试访问控制和切换用户身份的新项目目录:
-
如果您还没有打开,请在本地计算机上打开一个终端外壳。
-
如果您正在使用 Internet 计算机项目,请切换到您用于 Internet 计算机项目的文件夹。
-
通过运行以下命令创建一个新项目:
dfx new access_hello
-
通过运行以下命令切换到您的项目目录:
cd access_hello
修改默认程序
在本教程中,您将使用具有分配和检索角色功能的程序替换模板源代码文件。
修改默认程序:
-
src/access_hello/main.mo
在文本编辑器中打开文件并删除现有内容。 -
将以下示例代码复制并粘贴到文件中:
// Import base modules import AssocList "mo:base/AssocList"; import Error "mo:base/Error"; import List "mo:base/List"; shared({ caller = initializer }) actor class() { // Establish role-based greetings to display public shared({ caller }) func greet(name : Text) : async Text { if (has_permission(caller, #assign_role)) { return "Hello, " # name # ". You have a role with administrative privileges." } else if (has_permission(caller, #lowest)) { return "Welcome, " # name # ". You have an authorized account. Would you like to play a game?"; } else { return "Greetings, " # name # ". Nice to meet you!"; } }; // Define custom types public type Role = { #owner; #admin; #authorized; }; public type Permission = { #assign_role; #lowest; }; private stable var roles: AssocList.AssocList<Principal, Role> = List.nil(); private stable var role_requests: AssocList.AssocList<Principal, Role> = List.nil(); func principal_eq(a: Principal, b: Principal): Bool { return a == b; }; func get_role(pal: Principal) : ?Role { if (pal == initializer) { ?#owner; } else { AssocList.find<Principal, Role>(roles, pal, principal_eq); } }; // Determine if a principal has a role with permissions func has_permission(pal: Principal, perm : Permission) : Bool { let role = get_role(pal); switch (role, perm) { case (?#owner or ?#admin, _) true; case (?#authorized, #lowest) true; case (_, _) false; } }; // Reject unauthorized user identities func require_permission(pal: Principal, perm: Permission) : async () { if ( has_permission(pal, perm) == false ) { throw Error.reject( "unauthorized" ); } }; // Assign a new role to a principal public shared({ caller }) func assign_role( assignee: Principal, new_role: ?Role ) : async () { await require_permission( caller, #assign_role ); switch new_role { case (?#owner) { throw Error.reject( "Cannot assign anyone to be the owner" ); }; case (_) {}; }; if (assignee == initializer) { throw Error.reject( "Cannot assign a role to the canister owner" ); }; roles := AssocList.replace<Principal, Role>(roles, assignee, principal_eq, new_role).0; role_requests := AssocList.replace<Principal, Role>(role_requests, assignee, principal_eq, null).0; }; public shared({ caller }) func request_role( role: Role ) : async Principal { role_requests := AssocList.replace<Principal, Role>(role_requests, caller, principal_eq, ?role).0; return caller; }; // Return the principal of the message caller/user identity public shared({ caller }) func callerPrincipal() : async Principal { return caller; }; // Return the role of the message caller/user identity public shared({ caller }) func my_role() : async ?Role { return get_role(caller); }; public shared({ caller }) func my_role_request() : async ?Role { AssocList.find<Principal, Role>(role_requests, caller, principal_eq); }; public shared({ caller }) func get_role_requests() : async List.List<(Principal,Role)> { await require_permission( caller, #assign_role ); return role_requests; }; public shared({ caller }) func get_roles() : async List.List<(Principal,Role)> { await require_permission( caller, #assign_role ); return roles; }; };
让我们来看看这个程序的几个关键元素:
-
您可能会注意到该
greet
函数是greet
您在之前教程中看到的函数的变体。但是,在此程序中,该
greet
函数使用消息调用者来确定应应用的权限,并根据与调用者关联的权限,确定要显示的问候语。 -
该程序定义了两种自定义类型——一种是 for
Roles
,一种是 forPermissions
。 -
该
assign_roles
函数使消息调用者能够为与身份关联的主体分配角色。 -
该
callerPrincipal
函数使您能够返回与身份关联的主体。 -
该
my_role
函数使您能够返回与身份关联的角色。
-
-
保存更改并关闭
main.mo
文件以继续。
启动本地网络
在构建access_hello
项目之前,您需要连接到 Internet 计算机网络,要么在您的开发环境中本地运行,要么在您可以访问的子网上远程运行。
在本地启动网络:
-
在本地计算机上打开一个新的终端窗口或选项卡。
-
如有必要,导航到项目的根目录。
-
通过运行以下命令在本地计算机上启动 Internet 计算机网络:
dfx start --background
在本地 Internet 计算机网络完成其启动操作后,您可以继续下一步。
注册、构建和部署应用程序
在您连接到在您的开发环境中本地运行的 Internet 计算机网络后,您可以通过运行该dfx deploy
命令一步注册、构建和部署您的应用程序。也可以独立地使用单独执行的每个步骤
-
如果需要,请检查您是否仍在项目的根目录中。
-
access_hello
通过运行以下命令注册、构建和部署后端程序:dfx deploy access_hello
Creating a wallet canister on the local network. The wallet canister on the "local" network for user "default" is "rwlgt-iiaaa-aaaaa-aaaaa-cai" Deploying: access_hello Creating canisters... Creating canister "access_hello"... "access_hello" canister created with canister id: "rrkah-fqaaa-aaaaa-aaaaq-cai" Building canisters... Installing canisters... Installing code for canister access_hello, with canister_id rrkah-fqaaa-aaaaa-aaaaq-cai Deployed canisters.
检查当前身份上下文
在我们创建任何其他身份之前,让我们查看与您的default
身份相关联的主要标识符以及您身份的周期钱包default
。在 Internet 计算机上,主体是用户、容器、节点或子网的内部代表。主体的文本表示是您在使用主体数据类型时看到的外部标识符。
查看您当前的身份和原则:
-
通过运行以下命令验证当前活动的身份:
dfx identity whoami
该命令显示类似于以下内容的输出:
default
-
default
通过运行以下命令检查用户身份的主体:dfx identity get-principal
该命令显示类似于以下内容的输出:
zen7w-sjxmx-jcslx-ey4hf-rfxdq-l4soz-7ie3o-hti3o-nyoma-nrkwa-cqe
-
default
通过运行以下命令检查与用户身份关联的角色:dfx canister --wallet=$(dfx identity get-wallet) call access_hello my_role
该命令显示类似于以下内容的输出:
(opt variant { owner })
创建新的用户身份
为了开始测试我们程序中的访问控制,让我们创建一些新的用户身份并将这些用户分配到不同的角色。
要创建新的用户身份:
-
如果需要,请检查您是否仍在项目目录中。
-
通过运行以下命令创建新的管理用户身份:
dfx identity new ic_admin
该命令显示类似于以下内容的输出:
Creating identity: "ic_admin". Created identity: "ic_admin".
-
调用该
my_role
函数以查看您的新用户身份尚未分配给任何角色。dfx --identity ic_admin canister call access_hello my_role
该命令显示类似于以下内容的输出:
Creating a wallet canister on the local network. The wallet canister on the "local" network for user "ic_admin" is "ryjl3-tyaaa-aaaaa-aaaba-cai" (null)
-
通过运行以下命令,切换当前活动的身份上下文以使用新的
ic_admin
用户身份并显示与ic_admin
用户关联的主体:dfx identity use ic_admin && dfx identity get-principal
该命令显示类似于以下内容的输出:
Using identity: "ic_admin". c5wa6-3irl7-tuxuo-4vtyw-xsnhw-rv2a6-vcmdz-bzkca-vejmd-327zo-wae
-
access_hello
通过运行以下命令检查用于调用容器的主体:dfx canister call access_hello callerPrincipal
该命令显示类似于以下内容的输出:
(principal "ryjl3-tyaaa-aaaaa-aaaba-cai")
默认情况下,cycles 钱包标识符是用于调用
access_hello
容器中方法的主体。然而,为了说明访问控制,我们想使用与用户上下文相关联的主体,而不是周期钱包。不过,在我们进入这一步之前,让我们为ic_admin
用户分配一个角色。为此,我们需要切换到default
具有该owner
角色的用户身份。
为身份分配角色
将管理员角色分配给 ic_admin 身份:
-
default
通过运行以下命令,将当前活动的身份上下文切换为使用用户身份:dfx identity use default
-
通过使用 Candid 语法运行类似于以下的命令,为
ic_admin
主体分配admin
角色:dfx canister --wallet=$(dfx identity get-wallet) call access_hello assign_role '((principal "c5wa6-3irl7-tuxuo-4vtyw-xsnhw-rv2a6-vcmdz-bzkca-vejmd-327zo-wae"),opt variant{admin})'
+ 或者,您可以重新运行命令以调用my_role
函数来验证角色分配。
dfx --identity ic_admin canister call access_hello my_role
+ 该命令显示类似于以下内容的输出:
(opt variant { admin })
-
通过运行以下命令,使用您刚刚分配角色
greet
的ic_admin
用户身份调用该函数admin
:dfx --identity ic_admin canister call access_hello greet "Internet Computer Admin"
该命令显示类似于以下内容的输出:
( "Hello, Internet Computer Admin. You have a role with administrative privileges.", )
添加授权用户身份
此时,您有一个default
带有owner
角色的ic_admin
用户身份和一个带有角色的用户身份admin
。让我们添加另一个用户身份并将其分配给authorized
角色。但是,对于此示例,我们将使用环境变量来存储用户的主体。
添加新的授权用户身份:
-
如果需要,请检查您是否仍在项目目录中。
-
通过运行以下命令创建新的授权用户身份:
dfx identity new alice_auth
该命令显示类似于以下内容的输出:
Creating identity: "alice_auth". Created identity: "alice_auth".
-
alice_auth
通过运行以下命令,将当前活动的身份上下文切换为使用新的用户身份:dfx identity use alice_auth
-
alice_auth
通过运行以下命令将用户的主体存储在环境变量中:ALICE_ID=$(dfx identity get-principal)
您可以通过运行以下命令来验证存储的主体:
echo $ALICE_ID
该命令显示类似于以下内容的输出:
b5quc-npdph-l6qp4-kur4u-oxljq-7uddl-vfdo6-x2uo5-6y4a6-4pt6v-7qe
-
通过运行以下命令,使用
ic_admin
身份将authorized
角色分配给alice_auth
:dfx --identity ic_admin canister call access_hello assign_role "(principal \"$ALICE_ID\", opt variant{authorized})"
-
调用该
my_role
函数以验证角色分配。dfx --identity alice_auth canister call access_hello my_role
该命令显示类似于以下内容的输出:
(opt variant { authorized })
-
通过运行以下命令,使用您刚刚分配角色
greet
的alice_auth
用户身份调用该函数authorized
:dfx canister call access_hello greet "Alice"
该命令显示类似于以下内容的输出:
( "Welcome, Alice. You have an authorized account. Would you like to play a game?", )
添加未经授权的用户身份
您现在已经看到了创建具有特定角色和权限的用户的简单示例。下一步是创建未分配给角色或授予任何特殊权限的用户身份。
添加未经授权的用户身份:
-
如果需要,请检查您是否仍在项目目录中。
-
如果需要,通过运行以下命令检查您当前活动的身份:
dfx identity whoami
-
通过运行以下命令创建新的用户身份:
dfx identity new bob_standard
该命令显示类似于以下内容的输出:
Creating identity: "bob_standard". Created identity: "bob_standard".
-
bob_standard
通过运行以下命令将用户的主体存储在环境变量中:BOB_ID=$(dfx --identity bob_standard identity get-principal)
-
尝试使用
bob_standard
身份分配角色。dfx --identity bob_standard canister call access_hello assign_role "(principal \"$BOB_ID\", opt variant{authorized})"
此命令返回
unauthorized
错误。 -
尝试使用
default
用户身份来分配bob_standard
的owner
运行以下命令的作用:dfx --identity default canister --wallet=$(dfx --identity default identity get-wallet) call access_hello assign_role "(principal \"$BOB_ID\", opt variant{owner})"
此命令失败,因为无法为用户分配
owner
角色。 -
通过运行以下命令,
greet
使用bob_standard
用户身份调用该函数:dfx --identity bob_standard canister --no-wallet call access_hello greet "Bob"
该命令显示类似于以下内容的输出:
("Greetings, Bob. Nice to meet you!")
为多个命令设置用户身份
到目前为止,您已经了解了如何为单个命令创建和切换用户身份。您还可以指定要使用的用户身份,然后在该用户身份的上下文中运行多个命令。
要在一个用户身份下运行多个命令:
-
如果需要,请检查您是否仍在项目目录中。
-
通过运行以下命令列出当前可用的用户身份:
dfx identity list
该命令会显示类似于以下内容的输出,并带有指示当前活动用户身份的星号。
alice_auth bob_standard default * ic_admin
在此示例中,
default
除非您明确选择不同的身份,否则将使用用户身份。 -
从列表中选择一个新用户身份,并通过运行类似于以下内容的命令使其成为活动用户上下文:
dfx identity use ic_admin
+ 该命令显示类似于以下内容的输出:
Using identity: "ic_admin".
如果您重新运行该
dfx identity list
命令,ic_admin
用户身份将显示一个星号,以指示它是当前活动的用户上下文。您现在可以使用选定的用户身份运行命令,而无需
--identity
在命令行上指定。
停止本地网络
在您完成对程序的试验和使用身份后,您可以停止本地 Internet 计算机网络,使其不会继续在后台运行。
要停止本地网络:
-
在显示网络操作的终端中,按 Control-C 可中断本地网络进程。
-
通过运行以下命令停止 Internet 计算机网络:
dfx stop
接受来自钱包的周期
在做本地开发的时候,可以使用项目中的默认钱包发送周期,查看自己的周期余额。但是那些需要接收和销毁这些周期以执行其功能并向用户提供服务的程序呢?本教程提供了一个简单的示例来说明如何将接收周期和检查周期余额的函数添加到默认模板程序中。
此示例包含以下内容:
- 该
wallet_balance
功能使您能够检查容器的当前循环平衡。 - 该
wallet_receive
函数使程序能够接受发送到容器的循环。 - 该
greet
函数接受文本参数并在终端中显示问候语。 - 该
owner
函数返回消息调用者使用的主体。
在你开始之前
在开始本教程之前,请验证以下内容:
- 您已经下载并安装了 DFINITY Canister SDK 包
- 如果您使用 Visual Studio Code 作为 IDE,您已经为 Motoko 安装了 Visual Studio Code 插件。
- 您已停止在本地计算机上运行的任何 Internet 计算机网络进程。
创建一个新项目
创建一个用于测试访问控制和切换用户身份的新项目目录:
-
如果您还没有打开,请在本地计算机上打开一个终端外壳。
-
如果您正在使用 Internet 计算机项目,请切换到您用于 Internet 计算机项目的文件夹。
-
通过运行以下命令创建一个新项目:
dfx new cycles_hello
-
通过运行以下命令切换到您的项目目录:
cd cycles_hello
修改默认程序
对于本教程,您将修改模板源代码以包含用于接受周期和检查周期平衡的新函数。
修改默认程序:
-
src/cycles_hello/main.mo
在文本编辑器中打开文件并删除现有内容。 -
将以下示例代码复制并粘贴到文件中:
import Nat64 "mo:base/Nat64"; import Cycles "mo:base/ExperimentalCycles"; shared(msg) actor class HelloCycles ( capacity: Nat ) { var balance = 0; // Return the current cycle balance public shared(msg) func wallet_balance() : async Nat { return balance; }; // Return the cycles received up to the capacity allowed public func wallet_receive() : async { accepted: Nat64 } { let amount = Cycles.available(); let limit : Nat = capacity - balance; let accepted = if (amount <= limit) amount else limit; let deposit = Cycles.accept(accepted); assert (deposit == accepted); balance += accepted; { accepted = Nat64.fromNat(accepted) }; }; // Return the greeting public func greet(name : Text) : async Text { return "Hello, " # name # "!"; }; // Return the principal of the caller/user identity public shared(msg) func owner() : async Principal { let currentOwner = msg.caller; return currentOwner; }; };
让我们来看看这个程序的几个关键元素:
- 该程序导入了一个 Motoko 基础库
ExperimentalCycles
——它提供了使用循环的基本功能。 - 该程序使用一个
actor class
而不是单个actor,因此它可以有多个actor实例来接受capacity
所有实例的不同循环量,最多为a 。 - 将
capacity
作为参数传递给actor 类。 - 在
msg.caller
识别与呼叫相关联的主体。
- 该程序导入了一个 Motoko 基础库
-
保存更改并关闭
main.mo
文件以继续。
启动本地网络
在构建access_hello
项目之前,您需要连接到 Internet 计算机网络,要么在您的开发环境中本地运行,要么在您可以访问的子网上远程运行。
在本地启动网络:
-
在本地计算机上打开一个新的终端窗口或选项卡。
-
如有必要,导航到项目的根目录。
-
通过运行以下命令在本地计算机上启动 Internet 计算机网络:
dfx start --clean --background
在本地 Internet 计算机网络完成其启动操作后,您可以继续下一步。
注册、构建和部署应用程序
在您连接到在您的开发环境中本地运行的 Internet 计算机网络后,您可以在本地注册、构建和部署您的应用程序。
在本地部署应用程序:
-
如果需要,请检查您是否仍在项目的根目录中。
-
通过运行以下命令注册、构建和部署您的应用程序:
dfx deploy --argument '(360000000000)'
此示例将
capacity
容器的 设置为 360,000,000,000 次循环。的dfx deploy
命令输出然后显示有关的操作它执行,包括与此本地项目和钱包容器标识符创建的钱夹容器相关联的身份信息。例如:
Deploying all canisters. Creating canisters... Creating canister "cycles_hello"... Creating the canister using the wallet canister... Creating a wallet canister on the local network. The wallet canister on the "local" network for user "default" is "rwlgt-iiaaa-aaaaa-aaaaa-cai" "cycles_hello" canister created with canister id: "rrkah-fqaaa-aaaaa-aaaaq-cai" Creating canister "cycles_hello_assets"... Creating the canister using the wallet canister... "cycles_hello_assets" canister created with canister id: "ryjl3-tyaaa-aaaaa-aaaba-cai" Building canisters... Building frontend... Installing canisters... Installing code for canister cycles_hello, with canister_id rrkah-fqaaa-aaaaa-aaaaq-cai Installing code for canister cycles_hello_assets, with canister_id ryjl3-tyaaa-aaaaa-aaaba-cai Authorizing our identity (default) to the asset canister... Uploading assets to asset canister... Deployed canisters.
测试应用程序
在本地 Internet 计算机网络上部署应用程序后,您可以试验钱包功能并使用dfx canister call
命令测试您的程序。
要测试应用程序:
-
default
通过运行以下命令检查用户身份的主体:dfx canister call cycles_hello owner
该命令针对当前身份显示类似于以下内容的输出:
(principal "g3jww-sbmtm-gxsag-4mecu-72yc4-kef5v-euixq-og2kd-sav2v-p2sb3-pae")
如果您尚未更改用于运行该
dfx deploy
命令的身份,则应通过运行该dfx identity get-principal
命令获得相同的主体。这很重要,因为您必须是钱包容器的所有者才能执行某些任务,例如发送周期或授予其他保管人身份发送周期的权限。 -
通过运行以下命令检查初始钱包周期余额:
dfx canister call cycles_hello wallet_balance
您尚未向容器发送任何循环,因此该命令显示以下余额:
(0)
-
cycles_hello
通过运行类似于以下的命令,使用容器主体将一些周期从您的默认钱包容器发送到容器:dfx canister call rwlgt-iiaaa-aaaaa-aaaaa-cai wallet_send '(record { canister = principal "rrkah-fqaaa-aaaaa-aaaaq-cai"; amount = (256000000000:nat64); } )'
-
如果您指定的数量低于允许的容量,或者您在运行命令时指定的数量,则调用该
wallet_balance
函数以查看cycles_hello
容器具有您传输的周期数。capacity``dfx deploy
dfx canister call cycles_hello wallet_balance
该命令显示类似于以下内容的输出:
(256_000_000_000)
-
wallet_balance
通过运行类似于以下的命令来调用该函数以查看默认钱包中的周期数:dfx canister call rwlgt-iiaaa-aaaaa-aaaaa-cai wallet_balance
该命令使用 Candid 格式返回您指定为记录的钱包容器标识符的余额。例如,该命令可能会显示一个包含一个
amount
字段(由 hash 表示3_573_748_184
)和 97,738,624,621,042 个周期的余额的记录,如下所示:(record { 3_573_748_184 = 97_738_624_621_042 })
对于这个简单的教程,周期仅从默认钱包容器中的余额中消耗,而不是从
cycles_hello
容器中消耗。 -
greet
通过运行类似于以下的命令来调用该函数:dfx canister call cycles_hello greet '("from DFINITY")'
-
重新调用该
wallet_balance
函数以查看从您的默认钱包中扣除的周期数:dfx canister call rwlgt-iiaaa-aaaaa-aaaaa-cai wallet_balance
例如,您可能会得到与此类似的结果:
(记录{ 3_573_748_184 = 97_638_622_179_500 })
停止本地网络
在您完成对程序的试验后,您可以停止本地 Internet 计算机网络,使其不会继续在后台运行。
要停止本地网络:
-
在显示网络操作的终端中,按 Control-C 可中断本地网络进程。
-
通过运行以下命令停止 Internet 计算机网络:
dfx stop
eturn currentOwner;
};
};
让我们来看看这个程序的几个关键元素:
- 该程序导入了一个 Motoko 基础库`ExperimentalCycles`——它提供了使用循环的基本功能。
- 该程序使用一个`actor class`而不是单个actor,因此它可以有多个actor实例来接受`capacity`所有实例的不同循环量,最多为a 。
- 将`capacity`作为参数传递给actor 类。
- 在`msg.caller`识别与呼叫相关联的主体。
3. 保存更改并关闭`main.mo`文件以继续。
### 启动本地网络
在构建`access_hello`项目之前,您需要连接到 Internet 计算机网络,要么在您的开发环境中本地运行,要么在您可以访问的子网上远程运行。
在本地启动网络:
1. 在本地计算机上打开一个新的终端窗口或选项卡。
2. 如有必要,导航到项目的根目录。
3. 通过运行以下命令在本地计算机上启动 Internet 计算机网络:
```bash
dfx start --clean --background
在本地 Internet 计算机网络完成其启动操作后,您可以继续下一步。
注册、构建和部署应用程序
在您连接到在您的开发环境中本地运行的 Internet 计算机网络后,您可以在本地注册、构建和部署您的应用程序。
在本地部署应用程序:
-
如果需要,请检查您是否仍在项目的根目录中。
-
通过运行以下命令注册、构建和部署您的应用程序:
dfx deploy --argument '(360000000000)'
此示例将
capacity
容器的 设置为 360,000,000,000 次循环。的dfx deploy
命令输出然后显示有关的操作它执行,包括与此本地项目和钱包容器标识符创建的钱夹容器相关联的身份信息。例如:
Deploying all canisters. Creating canisters... Creating canister "cycles_hello"... Creating the canister using the wallet canister... Creating a wallet canister on the local network. The wallet canister on the "local" network for user "default" is "rwlgt-iiaaa-aaaaa-aaaaa-cai" "cycles_hello" canister created with canister id: "rrkah-fqaaa-aaaaa-aaaaq-cai" Creating canister "cycles_hello_assets"... Creating the canister using the wallet canister... "cycles_hello_assets" canister created with canister id: "ryjl3-tyaaa-aaaaa-aaaba-cai" Building canisters... Building frontend... Installing canisters... Installing code for canister cycles_hello, with canister_id rrkah-fqaaa-aaaaa-aaaaq-cai Installing code for canister cycles_hello_assets, with canister_id ryjl3-tyaaa-aaaaa-aaaba-cai Authorizing our identity (default) to the asset canister... Uploading assets to asset canister... Deployed canisters.
测试应用程序
在本地 Internet 计算机网络上部署应用程序后,您可以试验钱包功能并使用dfx canister call
命令测试您的程序。
要测试应用程序:
-
default
通过运行以下命令检查用户身份的主体:dfx canister call cycles_hello owner
该命令针对当前身份显示类似于以下内容的输出:
(principal "g3jww-sbmtm-gxsag-4mecu-72yc4-kef5v-euixq-og2kd-sav2v-p2sb3-pae")
如果您尚未更改用于运行该
dfx deploy
命令的身份,则应通过运行该dfx identity get-principal
命令获得相同的主体。这很重要,因为您必须是钱包容器的所有者才能执行某些任务,例如发送周期或授予其他保管人身份发送周期的权限。 -
通过运行以下命令检查初始钱包周期余额:
dfx canister call cycles_hello wallet_balance
您尚未向容器发送任何循环,因此该命令显示以下余额:
(0)
-
cycles_hello
通过运行类似于以下的命令,使用容器主体将一些周期从您的默认钱包容器发送到容器:dfx canister call rwlgt-iiaaa-aaaaa-aaaaa-cai wallet_send '(record { canister = principal "rrkah-fqaaa-aaaaa-aaaaq-cai"; amount = (256000000000:nat64); } )'
-
如果您指定的数量低于允许的容量,或者您在运行命令时指定的数量,则调用该
wallet_balance
函数以查看cycles_hello
容器具有您传输的周期数。capacity``dfx deploy
dfx canister call cycles_hello wallet_balance
该命令显示类似于以下内容的输出:
(256_000_000_000)
-
wallet_balance
通过运行类似于以下的命令来调用该函数以查看默认钱包中的周期数:dfx canister call rwlgt-iiaaa-aaaaa-aaaaa-cai wallet_balance
该命令使用 Candid 格式返回您指定为记录的钱包容器标识符的余额。例如,该命令可能会显示一个包含一个
amount
字段(由 hash 表示3_573_748_184
)和 97,738,624,621,042 个周期的余额的记录,如下所示:(record { 3_573_748_184 = 97_738_624_621_042 })
对于这个简单的教程,周期仅从默认钱包容器中的余额中消耗,而不是从
cycles_hello
容器中消耗。 -
greet
通过运行类似于以下的命令来调用该函数:dfx canister call cycles_hello greet '("from DFINITY")'
-
重新调用该
wallet_balance
函数以查看从您的默认钱包中扣除的周期数:dfx canister call rwlgt-iiaaa-aaaaa-aaaaa-cai wallet_balance
例如,您可能会得到与此类似的结果:
(记录{ 3_573_748_184 = 97_638_622_179_500 })
停止本地网络
在您完成对程序的试验后,您可以停止本地 Internet 计算机网络,使其不会继续在后台运行。
要停止本地网络:
-
在显示网络操作的终端中,按 Control-C 可中断本地网络进程。
-
通过运行以下命令停止 Internet 计算机网络:
dfx stop