这是 Google 课堂插件演示系列中的第一个 演示。
在本演示中,您将为开发 Web 应用并将其作为 Google 课堂插件发布奠定基础。后续演示步骤将扩展此应用。
在本演示中,您将完成以下操作:
- 为您的插件创建一个新的 Google Cloud 项目。
- 创建一个包含占位符登录按钮的框架 Web 应用。
- 为您的插件发布 Google Workspace Marketplace 商品详情。
完成后,您就可以安装插件并在 Google 课堂插件 iframe 中加载它。
前提条件
选择一种语言,查看相应的前提条件:
Python
我们的 Python 示例使用 Flask 框架。您可以从概览页面下载所有演示的完整
源代码。此特定演示的代码位于 /flask/01-basic-app/ 目录中。
如有必要,请安装 Python 3.7+,并确保 pip 可用。
python -m ensurepip --upgrade我们还建议您设置并激活新的 Python 虚拟环境。
python3 -m venv .classroom-addon-envsource .classroom-addon-env/bin/activate
下载的示例中的每个演示子目录都包含一个 requirements.txt。您可以使用 pip 快速安装所需的库。使用以下命令安装此演示所需的库。
cd flask/01-basic-apppip install -r requirements.txt
Node.js
我们的 Node.js 示例使用 Express 框架。您可以从概览页面下载所有演示的 完整 源代码。
如有必要,请安装 NodeJS v16.13+。
使用 npm 安装所需的节点模块。
npm installJava
我们的 Java 示例使用 Spring Boot 框架。您可以从概览页面下载所有演示的完整源代码 。
如果您的机器上尚未安装 Java 11+,请安装。
Spring Boot 应用可以使用 Gradle 或 Maven 来处理构建和管理依赖项。此示例包含 Maven 封装容器,可确保成功构建,而无需您安装 Maven 本身。
如需运行我们提供的示例,请在下载项目的目录中运行以下命令,确保您具备运行项目的前提条件。
java --version./mvnw --version
或者,在 Windows 系统中,运行以下命令:
java -versionmvnw.cmd --version
设置 Google Cloud 项目
对 Google 课堂 API 的访问权限和所需的身份验证方法由 Google Cloud 项目控制。以下说明将引导您完成创建和配置新项目以供插件使用的最少步骤。
创建项目
访问项目创建页面,创建一个新的 Google Cloud 项目。您可以为新项目提供任何名称。点击创建 。
新项目需要几分钟才能完全创建完毕。完成后,请 务必选择项目;您可以在屏幕顶部的项目选择器 下拉菜单中选择项目,也可以点击右上角 通知菜单中的选择项目。
将 Google Workspace Marketplace SDK 附加到 Google Cloud 项目
前往 API 库 浏览器。搜索 Google Workspace Marketplace SDK。您应该会在结果列表中看到该 SDK。
选择 Google Workspace Marketplace SDK 卡片,然后点击启用 。
配置 Google Workspace Marketplace SDK
Google Workspace Marketplace 提供了一个商品详情,用户和管理员可以通过该商品详情安装您的插件。配置 Marketplace SDK 的应用配置 和商品详情 以及 OAuth 权限请求页面 ,然后继续。
应用配置
前往 Marketplace SDK 的“应用配置”页面。 提供以下信息:
将应用公开范围 设置为
Public或Private。- 公开设置适用于最终将发布给最终用户的应用。公开应用必须经过审批流程才能发布给最终用户,但您可以指定用户以草稿 形式安装和测试该应用。这是一种预发布状态,可让您在发送插件以供审批之前对其进行测试和开发。
- 私享设置适用于内部测试和开发。专用应用只能由与创建项目的网域相同的网域中的用户安装。因此,您应该 仅在项目是在具有 Google Workspace 教育版 订阅的网域中创建的情况下,将公开范围设置为私享,否则您的测试用户将无法启动 Google 课堂插件。
如果您想将安装限制为仅限网域管理员,请将安装设置 设置为
Admin Only install。在应用集成 下,选择 Google 课堂插件 。系统会提示您输入附件设置 URI(安全) ;这是用户打开插件时您希望加载的网址。在本演示中,此网址应为
https://<your domain>/addon-discovery。允许使用的附件 URI 前缀 用于使用
courses.*.addOnAttachments.create和courses.*.addOnAttachments.patch方法验证AddOnAttachment中设置的 URI。验证是字面字符串前缀匹配,目前不允许使用通配符。至少添加内容服务器的根域名,例如https://localhost:5000/或https://cdn.myedtech.com/。添加与上一步中的 OAuth 同意屏幕中提供的 OAuth 范围 相同的范围。
在开发者链接 下,根据贵组织的情况填写相应字段。
商品详情
前往 Marketplace SDK 的“商品详情”页面。 提供以下信息:
- 在应用详情 下,添加一种语言,或展开已列出的 语言旁边的下拉菜单。提供应用名称和说明;这些内容会显示在插件的 Google Workspace Marketplace 商品详情页面上。 点击完成 以保存。
- 为您的插件选择一个类别 。
- 在图形资源下,为必填字段提供图片。这些内容可以稍后更改,现在可以作为占位符。
- 在支持链接下,提供所请求的网址。如果您在上一步中将应用公开范围设置为私享,则这些网址可以是 占位符。
如果您在上一步中将应用公开范围设置为私享 ,请点击 发布 ;您的应用会立即可供安装。如果您将 应用公开范围设置为公开,请在草稿测试人员区域 中为所有测试用户添加电子邮件地址,然后点击保存草稿。
OAuth 权限请求页面
OAuth 权限请求页面会在用户首次授权您的应用时显示。它会提示用户允许您的应用访问其个人信息和账号信息,具体取决于您启用的范围。
前往 OAuth 权限请求页面 创建页面。提供以下信息:
- 将用户类型 设置为外部 。点击创建 。
- 在下一页中,填写所需的应用详细信息和联系信息。 在已获授权的网域 下提供托管应用的任何网域。点击保存并继续 。
添加 Web 应用所需的任何 OAuth 范围 。如需详细了解范围及其用途,请参阅 OAuth 配置指南。
您必须请求至少一个以下范围,Google 才能发送
login_hint查询参数。如需详细了解此 行为,请参阅我们的 OAuth 配置指南:https://www.googleapis.com/auth/userinfo.email(已包含)https://www.googleapis.com/auth/userinfo.profile(已包含)
以下范围特定于 Google 课堂插件:
https://www.googleapis.com/auth/classroom.addons.teacherhttps://www.googleapis.com/auth/classroom.addons.student
此外,还应包含应用需要最终用户提供的任何其他 Google API 范围。
点击保存并继续 。
在测试用户 页面上列出所有测试账号的电子邮件地址。 点击保存并继续 。
确认设置正确无误,然后返回到信息中心。
安装插件
您现在可以使用 Marketplace SDK 的“商品详情”页面顶部的链接安装插件。点击页面顶部的在 Marketplace 中查看 以查看商品详情,然后选择安装 。
构建基本 Web 应用
设置一个包含两个路由的框架 Web 应用。后续演示步骤
将扩展此应用,因此现在只需为插件
/addon-discovery创建一个着陆页,并为我们的“公司网站”创建一个模拟索引页 /。

实现以下两个端点:
/:显示欢迎辞和一个按钮,用于关闭当前标签页和插件 iframe。/addon-discovery:显示欢迎消息和两个按钮:一个用于关闭插件 iframe,另一个用于在新标签页中打开网站。
请注意,我们添加了用于创建和关闭窗口或 iframe 的按钮。这些按钮演示了一种方法,用于在下一个演示中安全地将用户弹出到新标签页以进行授权。
创建实用程序脚本
创建 static/scripts 目录。创建一个新文件 addon-utils.js。添加以下两个函数。
/**
* Opens a given destination route in a new window. This function uses
* window.open() so as to force window.opener to retain a reference to the
* iframe from which it was called.
* @param {string} destinationURL The endpoint to open, or "/" if none is
* provided.
*/
function openWebsiteInNewTab(destinationURL = '/') {
window.open(destinationURL, '_blank');
}
/**
* Close the iframe by calling postMessage() in the host Classroom page. This
* function can be called directly when in a Classroom add-on iframe.
*
* Alternatively, it can be used to close an add-on iframe in another window.
* For example, if an add-on iframe in Window 1 opens a link in a new Window 2
* using the openWebsiteInNewTab function, you can call
* window.opener.closeAddonIframe() from Window 2 to close the iframe in Window
* 1.
*/
function closeAddonIframe() {
window.parent.postMessage({
type: 'Classroom',
action: 'closeIframe',
}, '*');
};
创建路由
实现 /addon-discovery 和 / 端点。
Python
设置应用目录
在本示例中,我们将应用逻辑构建为 Python 模块。这是我们提供的示例中的 webapp 目录。
为服务器模块创建一个目录,例如 webapp。将 static 目录移到模块目录中。同时在模块目录中创建一个 template 目录;您的 HTML 文件将放在此处。
构建服务器模块*
在模块目录中创建 __init__.py 文件,并添加以下导入和声明。
from flask import Flask
import config
app = Flask(__name__)
app.config.from_object(config.Config)
# Load other module script files. This import statement refers to the
# 'routes.py' file described below.
from webapp import routes
然后,创建一个文件来处理 Web 应用的路由。这是我们提供的示例中的 webapp/routes.py。在此文件中实现这两个路由。
from webapp import app
import flask
@app.route("/")
def index():
return flask.render_template("index.html",
message="You've reached the index page.")
@app.route("/classroom-addon")
def classroom_addon():
return flask.render_template(
"addon-discovery.html",
message="You've reached the addon discovery page.")
请注意,我们的路由都会将 message 变量传递给各自的 Jinja 模板。这有助于识别用户已到达哪个页面。
创建配置和启动文件
在应用的根目录中,创建 main.py 和 config.py 文件。在 config.py 中配置您的密钥。
import os
class Config(object):
# Note: A secret key is included in the sample so that it works.
# If you use this code in your application, replace this with a truly secret
# key. See https://flask.palletsprojects.com/quickstart/#sessions.
SECRET_KEY = os.environ.get(
'SECRET_KEY') or "REPLACE ME - this value is here as a placeholder."
在 main.py 文件中,导入您的模块并启动 Flask 服务器。
from webapp import app
if __name__ == "__main__":
# Run the application over HTTPs with a locally stored certificate and key.
# Defaults to https://localhost:5000.
app.run(
host="localhost",
ssl_context=("localhost.pem", "localhost-key.pem"),
debug=True)
Node.js
路由在 app.js 文件中使用以下代码行注册。
const websiteRouter = require('./routes/index');
const addonRouter = require('./routes/classroom-addon');
app.use('/', websiteRouter);
app.use('/addon-discovery', addonRouter);
打开 /01-basic-app/routes/index.js 并查看代码。当最终用户访问公司网站时,系统会访问此路由。该路由使用 index Handlebars 模板呈现响应,并向该模板传递一个包含 title 和 message 变量的数据对象。
router.get('/', function (req, res, next) {
res.render('index', {
title: 'Education Technology',
message: 'Welcome to our website!'
});
});
打开第二个路由 /01-basic-app/routes/classroom-addon.js 并查看代码。当最终用户访问插件时,系统会访问此路由。请注意,此路由使用 discovery Handlebars 模板以及 addon.hbs 布局,以不同于公司网站的方式呈现页面。
router.get('/', function (req, res, next) {
res.render('discovery', {
layout: 'addon.hbs',
title: 'Education Technology Classroom add-on',
message: `Welcome.`
});
});
Java
Java 代码示例使用模块来打包连续的演示步骤。由于这是第一个演示,因此代码位于 step_01_basic_app 模块下。我们不希望您使用模块来实现项目;相反,我们建议您在按照演示中的每个步骤操作时,基于单个项目进行构建。
创建一个控制器类(在此示例项目中为 Controller.java)来定义端点。在此文件中,从 spring-boot-starter-web 依赖项导入 @GetMapping 注释。
import org.springframework.web.bind.annotation.GetMapping;
在类定义上方添加 Spring 框架控制器注释,以指明类的用途。
@org.springframework.stereotype.Controller
public class Controller {
然后,实现两个路由和一个用于错误处理的额外路由。
/** Returns the index page that will be displayed when the add-on opens in a
* new tab.
* @param model the Model interface to pass error information that's
* displayed on the error page.
* @return the index page template if successful, or the onError method to
* handle and display the error message.
*/
@GetMapping(value = {"/"})
public String index(Model model) {
try {
return "index";
} catch (Exception e) {
return onError(e.getMessage(), model);
}
}
/** Returns the add-on discovery page that will be displayed when the iframe
* is first opened in Classroom.
* @param model the Model interface to pass error information that's
* displayed on the error page.
* @return the addon-discovery page.
*/
@GetMapping(value = {"/addon-discovery"})
public String addon_discovery(Model model) {
try {
return "addon-discovery";
} catch (Exception e) {
return onError(e.getMessage(), model);
}
}
/** Handles application errors.
* @param errorMessage message to be displayed on the error page.
* @param model the Model interface to pass error information to display on
* the error page.
* @return the error page.
*/
@GetMapping(value = {"/error"})
public String onError(String errorMessage, Model model) {
model.addAttribute("error", errorMessage);
return "error";
}
测试插件
启动服务器。然后,以其中一个 您的 教师测试用户的身份登录Google 课堂。前往课业 标签页,然后创建新的作业 。从插件 选择器中选择您的插件。iframe 随即打开 插件会加载您在 Marketplace SDK 的 应用配置页面中指定的 附件设置 URI。
恭喜!您已准备好继续执行下一步:使用 Google 单点登录登录用户。