更新应用程序以创建和列出 SharePoint Embedded Containers
在本练习中,你将更新现有项目以创建和检索 SharePoint Embedded Containers。
将登录功能添加到前端 React SPA
首先,将用户登录支持添加到 React SPA 前端项目。 这包括为 Microsoft 身份验证库添加一些配置代码 (MSAL) ,并使用必要的登录详细信息配置 Microsoft Graph 工具包。
设置身份验证提供程序
找到并打开 ./src/index.tsx 文件。
在现有导入之后添加以下导入:
import { Providers } from "@microsoft/mgt-element";
import { Msal2Provider } from "@microsoft/mgt-msal2-provider";
import * as Constants from "./common/constants"
import * as Scopes from "./common/scopes";
最后,在 React 呈现代码之前添加以下代码,为 Microsoft Graph 工具包配置全局 MSAL 提供程序:
Providers.globalProvider = new Msal2Provider({
clientId: Constants.CLIENT_ENTRA_APP_CLIENT_ID,
authority: Constants.CLIENT_ENTRA_APP_AUTHORITY,
scopes: [
...Scopes.GRAPH_OPENID_CONNECT_BASIC,
Scopes.GRAPH_USER_READ_ALL,
Scopes.GRAPH_FILES_READ_WRITE_ALL,
Scopes.GRAPH_SITES_READ_ALL,
Scopes.SPEMBEDDED_FILESTORAGECONTAINER_SELECTED
]
});
更新应用主页以处理登录和注销过程
找到并打开 ./src/App.tsx 文件。 删除文件的导入 logo.svg
,然后添加并更新剩余的导入以反映以下代码:
import React, {
useState, useEffect
} from "react";
import {
Providers,
ProviderState
} from "@microsoft/mgt-element";
import { Login } from "@microsoft/mgt-react";
import {
FluentProvider,
Text,
webLightTheme
} from "@fluentui/react-components"
import './App.css';
import {
InteractionRequiredAuthError,
PublicClientApplication
} from "@azure/msal-browser";
import * as Scopes from "./common/scopes";
import * as Constants from "./common/constants";
在导入语句之后,添加以下自定义 React 挂钩以获取当前用户的登录状态:
function useIsSignedIn() {
const [isSignedIn, setIsSignedIn] = useState(false);
useEffect(() => {
const updateState = async () => {
const provider = Providers.globalProvider;
setIsSignedIn(provider && provider.state === ProviderState.SignedIn);
};
Providers.onProviderUpdated(updateState);
updateState();
return () => {
Providers.removeProviderUpdatedListener(updateState);
}
}, []);
return isSignedIn;
}
现在,通过在 声明后添加以下代码来更新 App
:
const isSignedIn = useIsSignedIn();
const promptForContainerConsent = async (event: CustomEvent<undefined>): Promise<void> => {
const containerScopes = {
scopes: [Scopes.SPEMBEDDED_FILESTORAGECONTAINER_SELECTED],
redirectUri: `${window.location.protocol}://${window.location.hostname}${(window.location.port === '80' || window.location.port === '443') ? '' : ':' + window.location.port}`
};
const msalInstance = new PublicClientApplication({
auth: {
clientId: Constants.CLIENT_ENTRA_APP_CLIENT_ID,
authority: Constants.CLIENT_ENTRA_APP_AUTHORITY,
},
cache: {
cacheLocation: 'localStorage',
storeAuthStateInCookie: false,
},
});
msalInstance.acquireTokenSilent(containerScopes)
.then(response => {
console.log('tokenResponse', JSON.stringify(response));
})
.catch(async (error) => {
if (error instanceof InteractionRequiredAuthError) {
return msalInstance.acquireTokenPopup(containerScopes);
}
});
}
此代码将首先获取用户的当前登录状态,然后在用户选择呈现中的按钮后配置并获取访问令牌。
最后,通过将组件的 语句替换为以下内容来更新组件的 return()
呈现:
return (
<FluentProvider theme={webLightTheme}>
<div className="App">
<Text size={900} weight='bold'>Sample SPA SharePoint Embedded App</Text>
<Login loginCompleted={promptForContainerConsent} />
<div>
</div>
</div>
</FluentProvider>
);
测试 React 应用的身份验证
现在,让我们测试客户端 React 应用,以确保身份验证正常工作。
在项目的根文件夹中的命令行中运行以下命令:
npm run start
该脚本将生成服务器端 & 客户端项目,启动它们,然后启动客户端项目的浏览器:
选择“ 登录 ”按钮,并使用具有 Microsoft 365 租户管理员访问权限 的工作和学校 帐户登录。
成功登录后,你将重定向回 React 应用,其中显示已登录用户的姓名和电子邮件:
在控制台中按 Ctrl + C 停止服务器。
添加列出并选择“容器”的功能
通过基本项目设置并配置为支持用户身份验证,现在让我们添加支持,以在租户分区中列出并选择“容器”。
容器管理是一种特权操作,需要必须获取服务器端的访问令牌。 首先,我们先创建服务器端 API 部件以支持 React 应用。
添加实用工具方法以检索 OBO 令牌以调用 Microsoft Graph
我们首先需要一个实用工具文件,以便使用现有凭据通过 OAuth2 代理流获取令牌。
创建一个新文件 ./server/auth.ts ,并向其添加以下代码:
import { ConfidentialClientApplication } from "@azure/msal-node";
require('isomorphic-fetch');
import * as MSGraph from '@microsoft/microsoft-graph-client';
import * as Scopes from './common/scopes';
export const getGraphToken = async (confidentialClient: ConfidentialClientApplication, token: string): Promise<[boolean, string | any]> => {
try {
const graphTokenRequest = {
oboAssertion: token,
scopes: [
Scopes.GRAPH_SITES_READ_ALL,
Scopes.SPEMBEDDED_FILESTORAGECONTAINER_SELECTED
]
};
const oboGraphToken = (await confidentialClient.acquireTokenOnBehalfOf(graphTokenRequest))!.accessToken;
return [true, oboGraphToken];
} catch (error: any) {
const errorResult = {
status: 500,
body: JSON.stringify({
message: `Unable to generate Microsoft Graph OBO token: ${error.message}`,
providedToken: token
})
};
return [false, errorResult];
}
}
这将采用配置的 ConfidentialClientApplication 和用户的 ID 令牌,并使用 MSAL 库请求可用于调用 Microsoft Graph 的新令牌。
将容器管理添加到服务器端 API 项目
现在,让我们创建处理程序,以获取使用 Microsoft Graph 返回回 React 应用的容器列表。 创建一个新文件 ./server/listContainers.ts 并向其添加以下导入:
import {
Request,
Response
} from "restify";
import * as MSAL from "@azure/msal-node";
require('isomorphic-fetch');
import * as MSGraph from '@microsoft/microsoft-graph-client';
import { getGraphToken } from "./auth";
接下来,添加以下代码以创建 MSAL ConfidentialClientApplication 的实例,该实例将用于获取 OBO 访问令牌:
const msalConfig: MSAL.Configuration = {
auth: {
clientId: process.env['API_ENTRA_APP_CLIENT_ID']!,
authority: process.env['API_ENTRA_APP_AUTHORITY']!,
clientSecret: process.env['API_ENTRA_APP_CLIENT_SECRET']!
},
system: {
loggerOptions: {
loggerCallback(loglevel: any, message: any, containsPii: any) {
console.log(message);
},
piiLoggingEnabled: false,
logLevel: MSAL.LogLevel.Verbose,
}
}
};
const confidentialClient = new MSAL.ConfidentialClientApplication(msalConfig);
创建并导出将执行以下操作的新函数:
- 验证请求包含具有
Authorization
访问令牌的标头。 - 使用该令牌和
ConfidentialClientApplication
获取可用于调用 Microsoft Graph 的 OBO 令牌。 - 使用 OBO 令牌创建用于调用 Microsoft Graph 的新
AuthenticationProvider
客户端。
export const listContainers = async (req: Request, res: Response) => {
if (!req.headers.authorization) {
res.send(401, { message: 'No access token provided.' });
return;
}
const [bearer, token] = (req.headers.authorization || '').split(' ');
const [graphSuccess, oboGraphToken] = await getGraphToken(confidentialClient, token);
if (!graphSuccess) {
res.send(200, oboGraphToken);
return;
}
const authProvider = (callback: MSGraph.AuthProviderCallback) => {
callback(null, oboGraphToken);
};
}
最后一步是创建 Microsoft Graph 客户端并请求具有特定 ContainerTypeId
集的所有容器。 在函数的右括号前添加以下代码即时性:
try {
const graphClient = MSGraph.Client.init({
authProvider: authProvider,
defaultVersion: 'beta'
});
const graphResponse = await graphClient.api(`storage/fileStorage/containers?$filter=containerTypeId eq ${process.env["CONTAINER_TYPE_ID"]}`).get();
res.send(200, graphResponse);
return;
} catch (error: any) {
res.send(500, { message: `Unable to list containers: ${error.message}` });
return;
}
将此新终结点添加到 restify 服务器。 找到并打开 ./server/index.ts 文件,将单个导入语句添加到现有导入的末尾,并向终结点添加 HTTP GET 请求的 /api/listContainers
侦听器:
import { listContainers } from "./listContainers";
...
server.get('/api/listContainers', async (req, res, next) => {
try {
const response = await listContainers(req, res);
res.send(200, response)
} catch (error: any) {
res.send(500, { message: `Error in API server: ${error.message}` });
}
next();
});
更新 React 项目以显示我们的容器
使用服务器端 API 设置,我们可以更新 React 项目,为用户提供选择现有容器或创建新的容器的界面。
首先创建一个新接口 ./src/common/IContainer.ts,其中包含以下内容来表示我们将从 Microsoft Graph 调用发送和接收的对象:
export interface IContainer {
id: string;
displayName: string;
containerTypeId: string;
createdDateTime: string;
}
创建一个新服务,该服务将用于调用我们的 API 终结点或从 React 应用直接调用 Microsoft Graph。 创建一个新文件 ./src/services/spembedded.ts ,并向其添加以下代码:
import { Providers, ProviderState } from '@microsoft/mgt-element';
import * as Msal from '@azure/msal-browser';
import * as Constants from './../common/constants';
import * as Scopes from './../common/scopes';
import { IContainer } from './../common/IContainer';
export default class SpEmbedded {
}
接下来,添加一个实用工具函数,以获取可用于调用 Microsoft Graph 的访问令牌。 这将创建一个 MSAL,我们将用于提交该 MSAL PublicClientApplication
来调用服务器端 API。 将以下函数添加到 SharePoint Embedded 类:
async getApiAccessToken() {
const msalConfig: Msal.Configuration = {
auth: {
clientId: Constants.CLIENT_ENTRA_APP_CLIENT_ID,
authority: Constants.CLIENT_ENTRA_APP_AUTHORITY,
},
cache: {
cacheLocation: 'localStorage',
storeAuthStateInCookie: false
}
};
const scopes: Msal.SilentRequest = {
scopes: [`api://${Constants.CLIENT_ENTRA_APP_CLIENT_ID}/${Scopes.SPEMBEDDED_CONTAINER_MANAGE}`],
prompt: 'select_account',
redirectUri: `${window.location.protocol}//${window.location.hostname}${(window.location.port === '80' || window.location.port === '443') ? '' : ':' + window.location.port}`
};
const publicClientApplication = new Msal.PublicClientApplication(msalConfig);
await publicClientApplication.initialize();
let tokenResponse;
try {
tokenResponse = await publicClientApplication.acquireTokenSilent(scopes);
return tokenResponse.accessToken;
} catch (error) {
if (error instanceof Msal.InteractionRequiredAuthError) {
tokenResponse = await publicClientApplication.acquireTokenPopup(scopes);
return tokenResponse.accessToken;
}
console.log(error)
return null;
}
};
添加以下 listContainers()
方法,该方法将调用服务器端 API 以获取所有容器的列表。 这将获取实用工具方法返回的访问令牌,并将其包含在对服务器端 API 的调用中。 回想一下,此访问令牌用于创建 MSAL ConfidentialClientApplication
以获取用于调用 Microsoft Graph 的 OBO 令牌。 该 OBO 令牌具有授予的更多权限,我们只能从服务器端调用获取这些权限:
async listContainers(): Promise<IContainer[] | undefined> {
const api_endpoint = `${Constants.API_SERVER_URL}/api/listContainers`;
if (Providers.globalProvider.state === ProviderState.SignedIn) {
const token = await this.getApiAccessToken();
const containerRequestHeaders = {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
};
const containerRequestOptions = {
method: 'GET',
headers: containerRequestHeaders
};
const response = await fetch(api_endpoint, containerRequestOptions);
if (response.ok) {
const containerResponse = await response.json();
return (containerResponse.value)
? (containerResponse.value) as IContainer[]
: undefined;
} else {
console.error(`Unable to list Containers: ${JSON.stringify(response)}`);
return undefined;
}
}
};
现在,创建一个新的 React 组件,用于处理所有容器任务和 UI。 创建一个新文件 ./src/components/containers.tsx,并向其添加以下代码:
import React, { useEffect, useState } from 'react';
import {
Button,
Dialog, DialogActions, DialogContent, DialogSurface, DialogBody, DialogTitle, DialogTrigger,
Dropdown, Option,
Input, InputProps, InputOnChangeData,
Label,
Spinner,
makeStyles, shorthands, useId
} from '@fluentui/react-components';
import type {
OptionOnSelectData,
SelectionEvents
} from '@fluentui/react-combobox'
import { IContainer } from "./../common/IContainer";
import SpEmbedded from '../services/spembedded';
const spe = new SpEmbedded();
const useStyles = makeStyles({
root: {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
...shorthands.padding('25px'),
},
containerSelector: {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
rowGap: '10px',
...shorthands.padding('25px'),
},
containerSelectorControls: {
width: '400px',
},
dialogContent: {
display: 'flex',
flexDirection: 'column',
rowGap: '10px',
marginBottom: '25px'
}
});
export const Containers = (props: any) => {
// BOOKMARK 1 - constants & hooks
// BOOKMARK 2 - handlers go here
// BOOKMARK 3 - component rendering
return (
);
}
export default Containers;
注意
// BOOKMARK #
请注意组件中的注释。 这些可确保在正确的位置添加代码。
第一步是获取容器列表。 首先,在 之前 // BOOKMARK 1
添加以下代码。 这将设置几个状态值来保存从服务器端 API 检索到的容器:
const [containers, setContainers] = useState<IContainer[]>([]);
const [selectedContainer, setSelectedContainer] = useState<IContainer | undefined>(undefined);
const containerSelector = useId('containerSelector');
接下来,在 后面 // BOOKMARK 1
添加以下 React 挂钩,以在页面加载时获取所有容器,并设置将跟踪它们的状态对象:
useEffect(() => {
(async () => {
const containers = await spe.listContainers();
if (containers) {
setContainers(containers);
}
})();
}, []);
在刚刚添加的 React 挂钩实现之后创建一个事件处理程序,当用户从我们将添加到 UX 的下拉列表控件中选择容器时会触发该处理程序:
const onContainerDropdownChange = (event: SelectionEvents, data: OptionOnSelectData) => {
const selected = containers.find((container) => container.id === data.optionValue);
setSelectedContainer(selected);
};
通过在代码后面将以下内容添加到 return()
方法, // BOOKMARK 3
来更新呈现。 这将创建一个 DropDown
控件和一个占位符,我们将在其中添加所选容器中的内容列表:
<div className={styles.root}>
<div className={styles.containerSelector}>
<Dropdown
id={containerSelector}
placeholder="Select a Storage Container"
className={styles.containerSelectorControls}
onOptionSelect={onContainerDropdownChange}>
{containers.map((option) => (
<Option key={option.id} value={option.id}>{option.displayName}</Option>
))}
</Dropdown>
</div>
{selectedContainer && (`[[TOOD]] container "${selectedContainer.displayName}" contents go here`)}
</div>
此代码使用最初创建组件时添加到组件的样式对象。 若要使用这些样式,请紧接在 语句前面 return()
添加以下代码并 // BOOKMARK 3
注释:
const styles = useStyles();
最后一步是将新 Containers
组件添加到应用。 找到并打开文件, ./src/App.tsx
并在现有导入后添加以下导入:
import Containers from "./components/containers";
在 <div></div>
组件的 return()
中找到标记。 将该标记替换为以下内容,以仅当用户登录时才添加我们的 Containers
组件:
<div>
{isSignedIn && (<Containers />)}
</div>
测试列表并选择“容器”
现在,让我们测试客户端 React 应用,了解在 React 应用中列出和显示容器的更改的效果。
在项目的根文件夹中的命令行中运行以下命令:
npm run start
当浏览器加载时,请使用你一直使用的相同 工作和学校 帐户登录。
登录后,页面将重新加载,并且应显示容器列表(如果之前已创建任何容器)。
选择容器时,请注意,条件逻辑将显示占位符,其中选择了容器的名称:
在控制台中按 Ctrl + C 停止服务器。
添加创建新容器的功能
在本部分中,你将更新服务器端 API 和 React 应用,以从 Web 应用程序创建新的包含。
首先,我们先创建服务器端 API 部件以支持 React 应用。
向服务器端 API 项目添加创建容器的支持
现在,让我们创建处理程序,以使用 Microsoft Graph 创建容器,以返回到我们的 React 应用。 创建一个新文件 ./server/createContainer.ts ,并向其添加以下导入:
import {
Request,
Response
} from "restify";
import * as MSAL from "@azure/msal-node";
require('isomorphic-fetch');
import * as MSGraph from '@microsoft/microsoft-graph-client';
import { getGraphToken } from "./auth";
接下来,添加以下代码以创建 MSAL ConfidentialClientApplication
的实例,该实例将用于获取 OBO 访问令牌:
const msalConfig: MSAL.Configuration = {
auth: {
clientId: process.env['API_ENTRA_APP_CLIENT_ID']!,
authority: process.env['API_ENTRA_APP_AUTHORITY']!,
clientSecret: process.env['API_ENTRA_APP_CLIENT_SECRET']!
},
system: {
loggerOptions: {
loggerCallback(loglevel: any, message: any, containsPii: any) {
console.log(message);
},
piiLoggingEnabled: false,
logLevel: MSAL.LogLevel.Verbose,
}
}
};
const confidentialClient = new MSAL.ConfidentialClientApplication(msalConfig);
创建并导出将执行以下操作的新函数:
- 验证请求包含具有
Authorization
访问令牌的标头。 - 使用该令牌和
ConfidentialClientApplication
获取可用于调用 Microsoft Graph 的 OBO 令牌。 - 使用 OBO 令牌创建用于调用 Microsoft Graph 的新
AuthenticationProvider
客户端。
export const createContainer = async (req: Request, res: Response) => {
if (!req.headers.authorization) {
res.send(401, { message: 'No access token provided.' });
return;
}
const [bearer, token] = (req.headers.authorization || '').split(' ');
const [graphSuccess, graphTokenRequest] = await getGraphToken(confidentialClient, token);
if (!graphSuccess) {
res.send(200, graphTokenRequest);
return;
}
const authProvider = (callback: MSGraph.AuthProviderCallback) => {
callback(null, graphTokenRequest);
};
}
最后一步是创建 Microsoft Graph 客户端并提交请求,以创建一个设置为特定 ContainerTypeId
的新容器集。 在函数的右括号前添加以下代码即时性:
try {
const graphClient = MSGraph.Client.init({
authProvider: authProvider,
defaultVersion: 'beta'
});
const containerRequestData = {
displayName: req.body!.displayName,
description: (req.body?.description) ? req.body.description : '',
containerTypeId: process.env["CONTAINER_TYPE_ID"]
};
const graphResponse = await graphClient.api(`storage/fileStorage/containers`).post(containerRequestData);
res.send(200, graphResponse);
return;
} catch (error: any) {
res.send(500, { message: `Failed to create container: ${error.message}` });
return;
}
将此新终结点添加到 restify 服务器。 找到并打开 ./server/index.ts 文件,将单个 import 语句添加到现有导入的末尾,并将 HTTP POST 请求的侦听器添加到 /api/createContainers
终结点:
import { createContainer } from "./createContainer";
...
server.post('/api/createContainer', async (req, res, next) => {
try {
const response = await createContainer(req, res);
res.send(200, response)
} catch (error: any) {
res.send(500, { message: `Error in API server: ${error.message}` });
}
next();
});
更新 React 项目以创建新的容器
找到并打开文件 ./src/services/spembedded.ts 并将以下代码添加到 类:
async createContainer(containerName: string, containerDescription: string = ''): Promise<IContainer | undefined> {
const api_endpoint = `${Constants.API_SERVER_URL}/api/createContainer`;
if (Providers.globalProvider.state === ProviderState.SignedIn) {
const token = await this.getApiAccessToken();
const containerRequestHeaders = {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
};
const containerRequestData = {
displayName: containerName,
description: containerDescription
};
const containerRequestOptions = {
method: 'POST',
headers: containerRequestHeaders,
body: JSON.stringify(containerRequestData)
};
const response = await fetch(api_endpoint, containerRequestOptions);
if (response.ok) {
const containerResponse = await response.json();
return containerResponse as IContainer;
} else {
console.error(`Unable to create container: ${JSON.stringify(response)}`);
return undefined;
}
}
};
此新方法类似于现有 listContainers()
方法,只不过它会创建一个新对象并将其作为 POST 提交到服务器端 API。
最后一步是更新组件 Containers
以更新 UI 以支持创建容器。 找到并打开 ./src/components/containers.tsx 文件。
对于此步骤,我们将使用 Fluent UI React Dialog
组件。 首先,在注释前面 // BOOKMARK 1
添加以下状态对象和 UI 组件 ID 对象:
const [dialogOpen, setDialogOpen] = useState(false);
const containerName = useId('containerName');
const [name, setName] = useState('');
const containerDescription = useId('containerDescription');
const [description, setDescription] = useState('');
const [creatingContainer, setCreatingContainer] = useState(false);
接下来,紧接在注释前面 // BOOKMARK 2
添加以下代码。 这些处理程序用于从 Input
将位于 中的 Dialog
组件更新新容器的名称和说明属性。 它们还用于处理用户选择创建容器的按钮:
const handleNameChange: InputProps["onChange"] = (event: React.ChangeEvent<HTMLInputElement>, data: InputOnChangeData): void => {
setName(data?.value);
};
const handleDescriptionChange: InputProps["onChange"] = (event: React.ChangeEvent<HTMLInputElement>, data: InputOnChangeData): void => {
setDescription(data?.value);
};
const onContainerCreateClick = async (event: React.MouseEvent<HTMLButtonElement>): Promise<void> => {
setCreatingContainer(true);
const newContainer = await spe.createContainer(name, description);
if (newContainer) {
setName('');
setDescription('');
setContainers(current => [...current, newContainer]);
setSelectedContainer(newContainer);
setDialogOpen(false);
} else {
setName('');
setDescription('');
}
setCreatingContainer(false);
}
最后,紧接在结束 </Dropdown>
元素之后添加以下 React 代码。 这将创建从按钮触发的 Fluent UI React Dialog
组件:
<Dialog open={dialogOpen} onOpenChange={(event, data) => setDialogOpen(data.open)}>
<DialogTrigger disableButtonEnhancement>
<Button className={styles.containerSelectorControls} appearance='primary'>Create a new storage Container</Button>
</DialogTrigger>
<DialogSurface>
<DialogBody>
<DialogTitle>Create a new storage Container</DialogTitle>
<DialogContent className={styles.dialogContent}>
<Label htmlFor={containerName}>Container name:</Label>
<Input id={containerName} className={styles.containerSelectorControls} autoFocus required
value={name} onChange={handleNameChange}></Input>
<Label htmlFor={containerDescription}>Container description:</Label>
<Input id={containerDescription} className={styles.containerSelectorControls} autoFocus required
value={description} onChange={handleDescriptionChange}></Input>
{creatingContainer &&
<Spinner size='medium' label='Creating storage Container...' labelPosition='after' />
}
</DialogContent>
<DialogActions>
<DialogTrigger disableButtonEnhancement>
<Button appearance="secondary" disabled={creatingContainer}>Cancel</Button>
</DialogTrigger>
<Button appearance="primary"
value={name}
onClick={onContainerCreateClick}
disabled={creatingContainer || (name === '')}>Create storage Container</Button>
</DialogActions>
</DialogBody>
</DialogSurface>
</Dialog>
测试创建新容器
现在,让我们测试客户端 React 应用,了解在 React 应用中创建容器的更改的效果。
在项目的根文件夹中的命令行中运行以下命令:
npm run start
当浏览器加载时,请使用你一直使用的相同 工作和学校 帐户登录。
登录后,页面将重新加载,现在应包含用于启动对话框的按钮:
选择“ 创建新存储容器 ”按钮以打开对话框。 请注意,在输入名称之前如何禁用该按钮:
输入容器的名称和说明,然后选择 “创建存储容器”。 创建容器时,按钮处于禁用状态,控件 Spinner
显示用户正在运行:
对话框消失后,你将看到新的选择器显示我们的新容器!
在控制台中按 Ctrl + C 停止服务器。
摘要
在本练习中,你更新了现有项目以创建和检索 SharePoint Embedded Containers。