arrow_back

使用 Go 和 Cloud Run 开发 REST API

登录 加入
欢迎加入我们的社区,一起测试和分享您的知识!
done
学习 700 多个动手实验和课程并获得相关技能徽章

使用 Go 和 Cloud Run 开发 REST API

实验 1 小时 universal_currency_alt 5 个积分 show_chart 中级
info 此实验可能会提供 AI 工具来支持您学习。
欢迎加入我们的社区,一起测试和分享您的知识!
done
学习 700 多个动手实验和课程并获得相关技能徽章

GSP761

Google Cloud 自定进度实验

Pet Theory 徽标

概览

对于无服务器 Cloud Run 开发课程中的实验,您需要通读一个虚构的业务场景,并协助其中的人物完成无服务器迁移计划。

12 年前,Lily 创办了 Pet Theory 连锁宠物医院。随着连锁医院的发展,Lily 花在与保险公司电话沟通的时间上变多了,而用在治疗宠物上的时间变少了。如果保险公司能够在线查看治疗费用总额,该有多好!

在本系列之前的一些实验中,计算机顾问 Ruby 和 DevOps 工程师 Patrick 将 Pet Theory 的客户数据库迁移到了云端无服务器 Firestore 数据库中,然后为客户开放了在线预约的访问权限。由于 Pet Theory 的运维团队只有一个人,他们需要一个无需大量持续维护的无服务器解决方案。

在本实验中,您将帮助 Ruby 和 Patrick 向保险公司授予对客户数据的访问权限,同时确保不暴露客户的个人身份信息 (PII)。您将使用 Cloud Run(属于无服务器产品)构建一个安全的表现层状态转换 (REST) API 网关。这将允许保险公司查看治疗总费用,而看不到客户的个人身份信息。

目标

在本实验中,您将执行以下操作:

  • 使用 Go 开发一个 REST API
  • 将测试客户数据导入到 Firestore 中
  • 将此 REST API 连接至 Firestore 数据库
  • 将此 REST API 部署至 Cloud Run

前提条件

这是一个中级实验,假设您熟悉 Cloud 控制台和 Cloud Shell 环境。本实验是一系列实验中的一个。完成之前的实验会有所帮助,但这不是硬性要求。

  • 将数据导入到无服务器数据库
  • 使用 Firebase 和 Firestore 构建无服务器 Web 应用
  • 构建可创建 PDF 文件的无服务器应用

设置和要求

点击“开始实验”按钮前的注意事项

请阅读以下说明。实验是计时的,并且您无法暂停实验。计时器在您点击开始实验后即开始计时,显示 Google Cloud 资源可供您使用多长时间。

此实操实验可让您在真实的云环境中开展实验活动,免受模拟或演示环境的局限。我们会为您提供新的临时凭据,让您可以在实验规定的时间内用来登录和访问 Google Cloud。

为完成此实验,您需要:

  • 能够使用标准的互联网浏览器(建议使用 Chrome 浏览器)。
注意:请使用无痕模式或无痕浏览器窗口运行此实验。这可以避免您的个人账号与学生账号之间发生冲突,这种冲突可能导致您的个人账号产生额外费用。
  • 完成实验的时间 - 请注意,实验开始后无法暂停。
注意:如果您已有自己的个人 Google Cloud 账号或项目,请不要在此实验中使用,以避免您的账号产生额外的费用。

如何开始实验并登录 Google Cloud 控制台

  1. 点击开始实验按钮。如果该实验需要付费,系统会打开一个弹出式窗口供您选择付款方式。左侧是实验详细信息面板,其中包含以下各项:

    • 打开 Google Cloud 控制台按钮
    • 剩余时间
    • 进行该实验时必须使用的临时凭据
    • 帮助您逐步完成本实验所需的其他信息(如果需要)
  2. 点击打开 Google Cloud 控制台(如果您使用的是 Chrome 浏览器,请右键点击并选择在无痕式窗口中打开链接)。

    该实验会启动资源并打开另一个标签页,显示登录页面。

    提示:请将这些标签页安排在不同的窗口中,并将它们并排显示。

    注意:如果您看见选择账号对话框,请点击使用其他账号
  3. 如有必要,请复制下方的用户名,然后将其粘贴到登录对话框中。

    {{{user_0.username | "<用户名>"}}}

    您也可以在实验详细信息面板中找到用户名

  4. 点击下一步

  5. 复制下面的密码,然后将其粘贴到欢迎对话框中。

    {{{user_0.password | "<密码>"}}}

    您也可以在实验详细信息面板中找到密码

  6. 点击下一步

    重要提示:您必须使用实验提供的凭据。请勿使用您的 Google Cloud 账号凭据。 注意:在本次实验中使用您自己的 Google Cloud 账号可能会产生额外费用。
  7. 继续在后续页面中点击以完成相应操作:

    • 接受条款及条件。
    • 由于该账号为临时账号,请勿添加账号恢复选项或双重验证。
    • 请勿注册免费试用。

片刻之后,系统会在此标签页中打开 Google Cloud 控制台。

注意:如需查看列有 Google Cloud 产品和服务的菜单,请点击左上角的导航菜单导航菜单图标

激活 Cloud Shell

Cloud Shell 是一种装有开发者工具的虚拟机。它提供了一个永久性的 5GB 主目录,并且在 Google Cloud 上运行。Cloud Shell 提供可用于访问您的 Google Cloud 资源的命令行工具。

  1. 点击 Google Cloud 控制台顶部的激活 Cloud Shell “激活 Cloud Shell”图标

如果您连接成功,即表示您已通过身份验证,且当前项目会被设为您的 PROJECT_ID 环境变量所指的项目。输出内容中有一行说明了此会话的 PROJECT_ID

Your Cloud Platform project in this session is set to YOUR_PROJECT_ID

gcloud 是 Google Cloud 的命令行工具。它已预先安装在 Cloud Shell 上,且支持 Tab 自动补全功能。

  1. (可选)您可以通过此命令列出活跃账号名称:
gcloud auth list
  1. 点击授权

  2. 现在,输出的内容应如下所示:

输出:

ACTIVE: * ACCOUNT: student-01-xxxxxxxxxxxx@qwiklabs.net To set the active account, run: $ gcloud config set account `ACCOUNT`
  1. (可选)您可以通过此命令列出项目 ID:
gcloud config list project

输出

[core] project = <project_ID>

输出示例

[core] project = qwiklabs-gcp-44776a13dea667a6 Note: For full documentation of gcloud, in Google Cloud, refer to the gcloud CLI overview guide.

Lily

Lily,Pet Theory 创始人

Ruby,您好!

还记得我们上周聊过吗?保险公司的文书工作和电话快压得我喘不过气来了。如果有一种方法能让保险代理高效安全地访问客户记录就好了。

目前的工作量让人难以为继。您能提供帮助吗?

Lily

Ruby

Ruby,软件顾问

Lily,您好!

我昨天跟 Patrick 一起吃午饭时,想到了一个计划来让授权第三方更方便安全地访问 Pet Theory 的数字记录。

我们将按四个步骤实施此计划:

  1. 构建一个简单的 REST API。
  2. 导入客户测试数据。
  3. 将此 REST API 连接至客户数据库。
  4. 向此 REST API 添加身份验证机制。

Patrick 和我具备完成前两个步骤的技能,所以很容易开始执行这个计划。我们打算在本周末之前做出一个可行的原型设计。

Ruby

帮助 Ruby 管理为 Pet Theory 构建 REST API 所需的活动。

任务 1. 启用 Google API

对于此实验,系统已为您启用 2 个 API:

名称 API
Cloud Build cloudbuild.googleapis.com
Cloud Run run.googleapis.com

任务 2. 开发 REST API

  1. 激活您的项目:
gcloud config set project $(gcloud projects list --format='value(PROJECT_ID)' --filter='qwiklabs-gcp')
  1. 克隆 pet-theory 代码库并访问其源代码:
git clone https://github.com/rosera/pet-theory.git && cd pet-theory/lab08
  1. 使用您常用的文本编辑器,或点击 Cloud Shell 边栏中的代码编辑器按钮,查看 go.modgo.sum 文件。

  2. 创建 main.go 文件并将以下内容添加到此文件中:

package main import ( "fmt" "log" "net/http" "os" ) func main() { port := os.Getenv("PORT") if port == "" { port = "8080" } http.HandleFunc("/v1/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "{status: 'running'}") }) log.Println("Pets REST API listening on port", port) if err := http.ListenAndServe(":"+port, nil); err != nil { log.Fatalf("Error launching Pets REST API server: %v", err) } } 注意: 在上面的代码中,您创建一个端点来测试该服务是否按预期上线并运行。通过在服务网址后附加“/v1/”,您可以验证应用是否按预期运行。Cloud Run 会部署容器,因此您需要提供一个容器定义。名为 `Dockerfile` 的文件告知 Cloud Run 使用哪个 Go 版本、在应用中包含哪些文件以及如何启动代码。
  1. 现在,创建一个名为 Dockerfile 的文件并向其中添加以下内容:
FROM gcr.io/distroless/base-debian12 WORKDIR /usr/src/app COPY server . CMD [ "/usr/src/app/server" ]

文件 server 是从 main.go 构建的执行二进制文件。

  1. 运行以下命令以构建二进制文件:
go build -o server
  1. 运行构建命令之后,确保必需的 Dockerfile 和 sever 文件位于同一目录中:
ls -la . ├── Dockerfile ├── go.mod ├── go.sum ├── main.go └── server

对于 Cloud Run 中大多数基于 Go 的应用,类似于上面的模板 Dockerfile 文件通常无需修改即可使用。

  1. 运行以下命令部署您的简单 REST API:
gcloud builds submit \ --tag gcr.io/$GOOGLE_CLOUD_PROJECT/rest-api:0.1

此命令使用您的代码构建一个容器并将其放入项目的 Container Registry 中。依次点击导航菜单 > Container Registry 即可看到此容器。如果没有看到 rest-api,请点击刷新

Container Registry

点击检查我的进度,验证您是否完成了上述任务。

使用 Cloud Build 创建映像
  1. 容器构建完成之后,运行以下代码进行部署:
gcloud run deploy rest-api \ --image gcr.io/$GOOGLE_CLOUD_PROJECT/rest-api:0.1 \ --platform managed \ --region {{{ project_0.default_region | "Filled in at lab startup." }}} \ --allow-unauthenticated \ --max-instances=2
  1. 部署完成后,您会看到类似如下的消息:
Service [rest-api] revision [rest-api-00001] has been deployed and is serving traffic at https://rest-api-[hash].a.run.app

点击检查我的进度,验证您是否完成了上述任务。

已部署 REST API 服务
  1. 点击消息末尾处的服务网址,将其在新的浏览器标签页中打开。在该网址的结尾处附加 /v1/,然后按 Enter 键。

您应会看到以下消息:

{&quot;status&quot; : &quot;running&quot;}

REST API 已上线且运行正常。原型服务可用之后,在下一部分中,您将使用该 API 从 Firestore 数据库中检索“客户”信息。

任务 3. 导入测试客户数据

Ruby

Ruby,软件顾问

Patrick,您好!

您还保存着我们之前创建的虚构客户数据吗?我们需要利用这些数据来进行测试。

您还记得我们是如何设置 Firestore 数据库并导入数据的吗?

Ruby

Patrick

Patrick,IT 管理员

Ruby,您好!

是的,我还留着那些测试数据。我今天会将它们迁移到 Firestore,您明天可以用来进行测试。

Patrick

Ruby 与 Patrick 之前创建了一个包含 10 个客户的测试数据库,其中包括针对每位客户的猫咪的建议治疗方案。

帮助 Patrick 配置此 Firestore 数据库并导入客户测试数据。首先,在项目中启用 Firestore。

  1. 返回 Cloud 控制台并依次点击导航菜单 > Firestore

  2. 点击创建数据库按钮。

  3. 点击原生模式按钮,然后点击继续

  4. 对于位置类型,选择区域

  5. 从可用列表中选择区域 ,然后点击创建数据库

等待数据库创建完毕,然后再继续操作。

点击检查我的进度,验证您是否完成了上述任务。

已创建 Firestore 数据库
  1. 将导入的文件迁移到为您创建的 Cloud Storage 存储桶中:
gsutil mb -c standard -l {{{ project_0.default_region | Region }}} gs://$GOOGLE_CLOUD_PROJECT-customer gsutil cp -r gs://spls/gsp645/2019-10-06T20:10:37_43617 gs://$GOOGLE_CLOUD_PROJECT-customer
  1. 现在,将这些数据导入到 Firebase:
gcloud beta firestore import gs://$GOOGLE_CLOUD_PROJECT-customer/2019-10-06T20:10:37_43617/

重新加载 Cloud 控制台浏览器以查看 Firestore 结果。

  1. 在 Firestore 下,点击“默认”下的 customers。您应该会看到已导入的宠物数据,请简单浏览一下。如果未看到任何数据,请试试刷新页面。

很好,现在已成功创建了 Firestore 数据库并填充了测试数据!

任务 4. 将此 REST API 连接至 Firestore 数据库

Ruby

Ruby,软件顾问

Lily,您好!

跟您讲一下进度:Patrick 和我完成了清单上的前两项任务。

现在我要开始设计 REST API 的结构,使它可以访问 Firestore 中的客户数据。

Ruby

Lily

Lily,Pet Theory 创始人

Ruby,您好!

太好了,Ruby!希望看到下一阶段正式启动。

Lily

在此部分中,您将帮助 Ruby 在 REST API 中创建另一个端点,如下所示:

https://rest-api-[hash].a.run.app/v1/customer/22530

例如,该网址应该返回 ID 为 22530 的客户(如果此客户存在于 Firestore 数据库中)所有提议的、接受的和拒绝的治疗方案的总费用:

{ "status": "success", "data": { "proposed": 1602, "approved": 585, "rejected": 489 } } 注意:如果数据库中不存在此客户,系统应会返回状态代码 404(未找到)和一条错误消息。

这一新功能需要通过一个软件包来访问 Firestore 数据库,并用另一个软件包处理跨域资源共享 (CORS)。

  1. 获取 $GOOGLE_CLOUD_PROJECT 环境变量的值
echo $GOOGLE_CLOUD_PROJECT
  1. 打开 pet-theory/lab08 目录下现有的 main.go 文件。
注意:使用为 $GOOGLE_CLOUD_PROJECT 显示的值更新 main.go 的内容。
  1. 将该文件的内容替换为以下代码,确保 PROJECT_ID 设置为
package main import ( "context" "encoding/json" "fmt" "log" "net/http" "os" "cloud.google.com/go/firestore" "github.com/gorilla/handlers" "github.com/gorilla/mux" "google.golang.org/api/iterator" ) var client *firestore.Client func main() { var err error ctx := context.Background() client, err = firestore.NewClient(ctx, "{{{ project_0.project_id | \"Filled in at lab startup\"}}}") if err != nil { log.Fatalf("Error initializing Cloud Firestore client: %v", err) } port := os.Getenv("PORT") if port == "" { port = "8080" } r := mux.NewRouter() r.HandleFunc("/v1/", rootHandler) r.HandleFunc("/v1/customer/{id}", customerHandler) log.Println("Pets REST API listening on port", port) cors := handlers.CORS( handlers.AllowedHeaders([]string{"X-Requested-With", "Authorization", "Origin"}), handlers.AllowedOrigins([]string{"https://storage.googleapis.com"}), handlers.AllowedMethods([]string{"GET", "HEAD", "POST", "OPTIONS", "PATCH", "CONNECT"}), ) if err := http.ListenAndServe(":"+port, cors(r)); err != nil { log.Fatalf("Error launching Pets REST API server: %v", err) } }
  1. 在该文件的末尾处添加处理程序支持:
func rootHandler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "{status: 'running'}") } func customerHandler(w http.ResponseWriter, r *http.Request) { id := mux.Vars(r)["id"] ctx := context.Background() customer, err := getCustomer(ctx, id) if err != nil { w.WriteHeader(http.StatusInternalServerError) fmt.Fprintf(w, `{"status": "fail", "data": '%s'}`, err) return } if customer == nil { w.WriteHeader(http.StatusNotFound) msg := fmt.Sprintf("`Customer \"%s\" not found`", id) fmt.Fprintf(w, fmt.Sprintf(`{"status": "fail", "data": {"title": %s}}`, msg)) return } amount, err := getAmounts(ctx, customer) if err != nil { w.WriteHeader(http.StatusInternalServerError) fmt.Fprintf(w, `{"status": "fail", "data": "Unable to fetch amounts: %s"}`, err) return } data, err := json.Marshal(amount) if err != nil { w.WriteHeader(http.StatusInternalServerError) fmt.Fprintf(w, `{"status": "fail", "data": "Unable to fetch amounts: %s"}`, err) return } fmt.Fprintf(w, fmt.Sprintf(`{"status": "success", "data": %s}`, data)) }
  1. 在该文件的末尾处添加客户支持:
type Customer struct { Email string `firestore:"email"` ID string `firestore:"id"` Name string `firestore:"name"` Phone string `firestore:"phone"` } func getCustomer(ctx context.Context, id string) (*Customer, error) { query := client.Collection("customers").Where("id", "==", id) iter := query.Documents(ctx) var c Customer for { doc, err := iter.Next() if err == iterator.Done { break } if err != nil { return nil, err } err = doc.DataTo(&c) if err != nil { return nil, err } } return &c, nil } func getAmounts(ctx context.Context, c *Customer) (map[string]int64, error) { if c == nil { return map[string]int64{}, fmt.Errorf("Customer should be non-nil: %v", c) } result := map[string]int64{ "proposed": 0, "approved": 0, "rejected": 0, } query := client.Collection(fmt.Sprintf("customers/%s/treatments", c.Email)) if query == nil { return map[string]int64{}, fmt.Errorf("Query is nil: %v", c) } iter := query.Documents(ctx) for { doc, err := iter.Next() if err == iterator.Done { break } if err != nil { return nil, err } treatment := doc.Data() result[treatment["status"].(string)] += treatment["cost"].(int64) } return result, nil }
  1. 保存该文件。

任务 6. 随堂测验

响应 `/v1/customer/` 模式网址的函数是哪一个 getAmounts customerHandler 向客户端返回“success”的语句是哪一条 fmt.Fprintf(w, `{"status": "fail", "data": "Unable to fetch amounts: %s"} fmt.Fprintf(w, fmt.Sprintf(`{"status": "success", "data": %s} 从 Firestore 数据库中读取数据的函数是哪两个 customerHandler 和 getCustomer getCustomer 和 getAmounts

任务 7. 部署新的修订版

  1. 对源代码进行重新构建:
go build -o server
  1. 为 REST API 构建一个新映像:
gcloud builds submit \ --tag gcr.io/$GOOGLE_CLOUD_PROJECT/rest-api:0.2

点击检查我的进度,验证已完成以下目标: 构建映像修订版 0.2

  1. 部署更新后的映像:
gcloud run deploy rest-api \ --image gcr.io/$GOOGLE_CLOUD_PROJECT/rest-api:0.2 \ --platform managed \ --region {{{ project_0.default_region | "Filled in at lab startup." }}} \ --allow-unauthenticated \ --max-instances=2 4. 部署完成后,您将看到与之前类似的消息。部署新版本后,您的 REST API 的网址没有发生变化: Service [rest-api] revision [rest-api-00002] has been deployed and is serving traffic at https://rest-api-[hash].a.run.app
  1. 返回到指向该网址(末尾处有 /v1/)的浏览器标签页。刷新并确保可以收到与以前同样的消息,这表示该 API 仍在运行。

{status&quot; : &quot;running&quot;}

  1. 在浏览器地址栏中,将 /customer/22530 附加到应用网址。您应该会收到此 JSON 响应,里面列出了客户提议、批准和拒绝的治疗方案的总金额:

{&quot;status&quot; : &quot;success&quot;, &quot;data&quot; :{&quot;proposed&quot; :1602, &quot;approved&quot; :585, &quot;reected&quot; :489}}

  1. 除了 22530,您还可以在此网址中输入其他几个客户 ID:
  • 34216
  • 70156(所有金额数应为 0)
  • 12345(客户/宠物不存在,应返回错误,例如 Query is nil

您构建了一个可从数据库中读取数据的可扩缩、低维护量的无服务器 REST API。

恭喜!

恭喜!在此实验中,您帮助 Ruby 和 Patrick 成功为 Pet Theory 构建了一个原型 REST API。您创建了连接至 Firestore 数据库的 REST API,并将其部署到了 Cloud Run。您还测试了此 API 以确保其按预期运行。

Google Cloud 培训和认证

…可帮助您充分利用 Google Cloud 技术。我们的课程会讲解各项技能与最佳实践,可帮助您迅速上手使用并继续学习更深入的知识。我们提供从基础到高级的全方位培训,并有点播、直播和虚拟三种方式选择,让您可以按照自己的日程安排学习时间。各项认证可以帮助您核实并证明您在 Google Cloud 技术方面的技能与专业知识。

上次更新手册的时间:2024 年 5 月 6 日

上次测试实验的时间:2024 年 5 月 6 日

版权所有 2024 Google LLC 保留所有权利。Google 和 Google 徽标是 Google LLC 的商标。其他所有公司名和产品名可能是其各自相关公司的商标。

此内容目前不可用

一旦可用,我们会通过电子邮件告知您

太好了!

一旦可用,我们会通过电子邮件告知您