使用跨域库从加载项访问 SharePoint 数据

构建 SharePoint 加载项时,通常必须整合来自各种源的数据。 但出于安全考虑,拦截机制会阻止同时与多个域进行通信。 大多数浏览器都会实施这些安全机制,使跨域进行客户端调用难以完成或无法完成。

当用户从加载项域请求页面时,客户端通信仅与该域绑定。 加载项可以从页面仅向同一域中的其他资源发起客户端调用。 但是,加载项通常需要来自其他域(例如 SharePoint 域)的资源来实现其方案。 在页面上的代码中,可以尝试向 SharePoint 域发出请求,而该请求会被浏览器拦截。 通常会看到“拒绝访问”错误。 该错误并不意味着你无权访问请求的资源,而最可能的是,无法向提及的资源发出请求。

使用跨域库时,加载项中的网页可以访问加载项域和 SharePoint 域中的数据。 跨域库是 SharePoint 网站上托管的 JavaScript 文件 (SP.RequestExecutor.js) 形式的客户端备用方案,可以在远程加载项中进行引用。 使用跨域库,可以通过代理在远程加载项页面中与多个域进行交互。 如果希望在客户端(而不是服务器)中运行加载项代码,并且如果 SharePoint 与远程基础结构之间存在连接障碍(如防火墙),不妨考虑使用此选项。

可以访问主机 Web 中的数据,例如,可以访问最终用户与之交互的列表,而无需考虑加载项。 或者可以访问加载项 Web 中的数据,例如专门为加载项预配的列表。 加载项还可以访问其他网站集和网站,只要加载项具有租户范围的权限并且通过使用加载项目录将其部署为批量安装。

注意

在本主题中,加载项域指托管加载项页的域。 这可以是提供程序托管的加载项中的远程 Web 应用程序的域,但是加载项页也可以位于加载项 Web 中的 SharePoint 上并可对主机 Web 域进行调用。 在后者中,加载项域为加载项 Web 的域。

本文中的主要示例显示了如何构建可读取加载项 Web 上的数据并将其显示在网页上的加载项。 后续步骤部分介绍了在主示例的基础上构建的更多方案。

先决条件

若要按照本文中的示例操作,需要具备以下各项:

使用跨域库读取加载项 Web 上的数据

本示例中,SharePoint 外部托管的简单页面使用代表性状态传输 (REST) 终结点读取 SharePoint 网站(加载项 Web)中的数据。 由于跨域库需要加载项 Web,因此从此方案开始是合理的。

若要读取加载项 Web 数据,必须执行下列操作:

  1. 创建一个 SharePoint 加载项和多个 Web 项目。

  2. 在加载项 Web 上创建列表项。 此步骤也会确保在用户部署加载项时,同时创建了加载项 Web。

  3. 创建一个使用跨域库读取列表项的加载项页面。

下图说明了显示加载项 Web 上的数据的网页。

跨域读取项示例结果屏幕

创建一个 SharePoint 加载项和多个 Web 项目

  1. 以管理员身份打开 Visual Studio。 (为此,请右键单击“开始”菜单上的“Visual Studio”图标,再选择“以管理员身份运行”。)

  2. 使用“SharePoint 加载项”模板创建一个新项目。 Visual Studio 中的“SharePoint 加载项”模板位于“模板”>“Visual C#”>“Office SharePoint”>“加载项”下。

  3. 提供要用于调试的 SharePoint 网站 URL。

  4. 选择“提供程序托管”作为你的加载项的托管选项。

    注意

    也可以在 SharePoint 托管的加载项中使用跨域库。 但是,在 SharePoint 托管的加载项中,加载项页面已经位于加载项 Web 上,在这种情况下,将无需跨域库读取列表项。 有关读取主机 Web 上的数据的 SharePoint 托管的加载项示例,请参阅本文后面的在 SharePoint 托管的加载项中使用跨域库(REST)或请参阅访问主机 Web 中的数据

在加载项 Web 上创建列表项

  1. 右键单击“解决方案资源管理器”中的 SharePoint 加载项项目。 选择“添加”>“新建项”

  2. 选择“Visual C# 项”>“Office/SharePoint”>“列表”。 将列表名称设置为“公告”

  3. 双击“公告”>“Elements.xml”。 粘贴以下 XML 节点作为“ListInstance”元素的子元素。

    <Data>
        <Rows>
            <Row>
                <Field Name="Title">Lorem ipsum 1</Field>
                <Field Name="Body">Sed ut perspiciatis, unde omnis iste...</Field>
            </Row>
            <Row>
                <Field Name="Title">Lorem ipsum 2</Field>
                <Field Name="Body">Sed ut perspiciatis, unde omnis iste...</Field>
            </Row>
        </Rows>
    </Data>
    

创建使用跨域库的加载项页面

  1. 在“解决方案资源管理器”的 Web 项目中,双击“Default.aspx”文件。

  2. 复制以下代码并将其粘贴到 Default.aspx 文件中。 该代码将执行以下任务:

    • 从 Microsoft CDN 加载 jQuery 库。

    • 提供结果的占位符。

    • 从查询字符串中提取加载项 Web URL。

    • 使用 jQuery 中的 getScript 函数加载跨域库 JavaScript。

      此函数将加载所需资源,然后加载指定的函数,以确保跨域库已加载并可供后续代码使用。

    • 实例化 RequestExecutor 对象。 默认情况下,RequestExecutor 使用外接程序 Web 作为上下文网站。

      注意

      (REST) 或对象 (JSOM)。 若要了解有关 AppContextSite 的更多信息,请参阅本文后面的访问主机 Web 中的数据

    • 向列表项终结点发起 REST 调用。

    • 处理成功完成操作,并在网页中显示列表项。

    • 处理错误,并在网页中显示错误消息。

  
<html>
    <head>
        <title>Cross-domain sample</title>
    </head>
    <body>
        <!-- This is the placeholder for the announcements -->
        <div id="renderAnnouncements"></div>
        <script 
            type="text/javascript" 
            src="//ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.2.min.js">
        </script>
        <script type="text/javascript">
          var hostweburl;
          var appweburl;

          // Load the required SharePoint libraries
          $(document).ready(function () {
            //Get the URI decoded URLs.
            hostweburl =
                decodeURIComponent(
                    getQueryStringParameter("SPHostUrl")
            );
            appweburl =
                decodeURIComponent(
                    getQueryStringParameter("SPAppWebUrl")
            );

            // resources are in URLs in the form:
            // web_url/_layouts/15/resource
            var scriptbase = hostweburl + "/_layouts/15/";

            // Load the js files and continue to the successHandler
            $.getScript(scriptbase + "SP.RequestExecutor.js", execCrossDomainRequest);
          });

          // Function to prepare and issue the request to get
          //  SharePoint data
          function execCrossDomainRequest() {
            // executor: The RequestExecutor object
            // Initialize the RequestExecutor with the add-in web URL.
            var executor = new SP.RequestExecutor(appweburl);

            // Issue the call against the add-in web.
            // To get the title using REST we can hit the endpoint:
            //      appweburl/_api/web/lists/getbytitle('listname')/items
            // The response formats the data in the JSON format.
            // The functions successHandler and errorHandler attend the
            //      sucess and error events respectively.
            executor.executeAsync(
                {
                  url:
                      appweburl +
                      "/_api/web/lists/getbytitle('Announcements')/items",
                  method: "GET",
                  headers: { "Accept": "application/json; odata=verbose" },
                  success: successHandler,
                  error: errorHandler
                }
            );
          }

          // Function to handle the success event.
          // Prints the data to the page.
          function successHandler(data) {
            var jsonObject = JSON.parse(data.body);
            var announcementsHTML = "";

            var results = jsonObject.d.results;
            for (var i = 0; i < results.length; i++) {
              announcementsHTML = announcementsHTML +
                  "<p><h1>" + results[i].Title +
                  "</h1>" + results[i].Body +
                  "</p><hr>";
            }

            document.getElementById("renderAnnouncements").innerHTML =
                announcementsHTML;
          }

          // Function to handle the error event.
          // Prints the error message to the page.
          function errorHandler(data, errorCode, errorMessage) {
            document.getElementById("renderAnnouncements").innerText =
                "Could not complete cross-domain call: " + errorMessage;
          }

          // Function to retrieve a query string value.
          // For production purposes you may want to use
          //  a library to handle the query string.
          function getQueryStringParameter(paramToRetrieve) {
            var params =
                document.URL.split("?")[1].split("&amp;");
            var strParams = "";
            for (var i = 0; i < params.length; i = i + 1) {
              var singleParam = params[i].split("=");
              if (singleParam[0] == paramToRetrieve)
                return singleParam[1];
            }
          }
        </script>
    </body>
</html>

生成并运行解决方案

  1. 按 F5 键。

    注意

    按 F5 键时,Visual Studio 会生成解决方案并部署加载项,同时还会打开加载项的权限页面。

  2. 选择“信任它”按钮。

  3. 选择“网站内容”页上的加载项图标。

如果更想获取可下载的代码示例,可从代码库获取以下内容:

解决方案疑难解答

如果看到此错误消息… 请尝试…
“很抱歉,访问你的网站时出现问题。此外,存在可以修复此错误的按钮,但是它不能解决该问题。” 你可能会在 Internet Explorer 中遇到安全区域的已知问题,请参阅跨 SharePoint 加载项中的不同 Internet Explorer 安全区域使用跨域库
“你的浏览器不支持所需功能。 请确保你使用的是 IE 8 或更高版本或其他新式浏览器。 请确保将“X-UA-Compatible”META 标记设置为“IE=8”或更高。” 跨域库要求使用 IE8 或更高版本的文档模式。 在某些场景中,文档模式默认设置为 IE7。 可以使用 Internet Explorer 开发人员工具来确定和更改页面的文档模式。 有关更多信息,请参阅定义文档兼容性
““Type”未定义。 此外,你的加载项使用 JavaScript 对象模型 (JSOM)。” JSOM 使用 Microsoft Ajax library 的 Type.registerNamespace 方法注册 SP 命名空间。 使用以下代码将引用从页面添加到 Microsoft Ajax library。

HTML <script type="text/javascript" src="//ajax.aspnetcdn.com/ajax/4.0/1/MicrosoftAjax.js"></script>

后续步骤:更多跨域库方案

本文介绍如何使用未托管在 SharePoint 上的外接程序页面查询 REST 终结点以读取外接程序 Web 数据。 您还可以浏览以下有关跨域库的方案和详细信息。

使用 JSOM 读取加载项 Web 数据

根据您的喜好,您可能想要使用 JSOM 而不是 REST 查询外接程序 Web 数据。 您必须完成其他任务才能与 JSOM 一起使用跨域库:

  • 参考外接程序页面中的 SharePoint JSOM。

  • 初始化 ProxyWebRequestExecutorFactory 对象并将其设置为上下文对象的工厂。

  • 访问 SharePoint 对象以读取列表数据。

  • 加载上下文中的对象并执行查询。

有关显示如何执行任务的代码示例,请参阅 SharePoint-Add-in-JSOM-CrossDomain

有关如何使用 JSOM 的更多信息,请参阅在 SharePoint 加载项中使用 JavaScript 对象模型 (JSOM)

访问主机 Web 中的数据

本页上的示例显示了如何读取加载项 Web 中的数据。 这非常适合作为入门示例,因为跨域库最初将加载项用作上下文网站。 但是,还有许多需要访问主机 Web 上的数据的场景。 访问主机 Web 上的数据需要执行一些任务:

  • 将主机 Web 设置为跨域库的上下文网站。

  • 为加载项提供相应的权限。

可以通过使用 AppContextSite 终结点 (REST) 或对象 (JSOM) 更改上下文网站。 以下代码示例显示了如何使用 REST 终结点更改上下文网站:

executor.executeAsync(
    {
        url:
            appweburl +
            "/_api/SP.AppContextSite(@target)/web/title?@target='" +
            hostweburl + "'",
        method: "GET",
        headers: { "Accept": "application/json; odata=verbose" },
        success: successHandler,
        error: errorHandler
    }
);

以下代码示例显示了如何使用 JSOM 更改上下文网站:

context = new SP.ClientContext(appweburl);
factory = new SP.ProxyWebRequestExecutorFactory(appweburl);
context.set_webRequestExecutorFactory(factory);
appContextSite = new SP.AppContextSite(context, hostweburl);

this.web = appContextSite.get_web();
context.load(this.web);

默认情况下,您的外接程序具有访问外接程序 Web 的权限,但不具有访问主机 Web 的权限。 以下示例显示了声明读取主机 Web 数据的权限请求的清单部分:

<AppPermissionRequests>
    <AppPermissionRequest 
        Scope="http://sharepoint/content/sitecollection/web" 
        Right="Read" />
</AppPermissionRequests>

请确保在加载项 Web 上创建一个资源(如空页面或列表)以强制预配加载项 Web,这需要使用跨域库。

跨网站集访问数据

通过使用跨域库,您可以以同一租户跨网站集访问数据。 以下是跨网站集访问数据需要完成的一些任务:

  • 添加一个权限请求以访问租户数据。

  • 在代码中,将上下文网站切换为您想要查询的网站集。

  • 将加载项添加到加载项目录。

  • 将加载项作为租户范围的加载项部署到网站。 有关如何部署作为租户范围的加载项的示例,请参阅在租户范围的加载项中使用跨域库 (REST)代码示例的说明。

您的外接程序还需要访问租户数据的权限。 以下示例显示了声明读取租户数据的权限请求的清单部分:

<AppPermissionRequests>
  <AppPermissionRequest 
    Scope="http://sharepoint/content/tenant" 
    Right="Read" />
</AppPermissionRequests>

若要在代码中切换上下文网站,请使用 AppContextSite 终结点 (REST) 或对象 (JSOM),如访问主机 Web 数据部分所述。

下面是 REST 终结点的提醒:/_api/SP。AppContextSite (@target) /web/title?@target='weburl',以及有关如何在 JSOM 中实例化对象的示例: appContextSite = new SP.AppContextSite(context, weburl);

作为开发人员,只能从加载项目录部署租户范围的加载项。 可以将加载项目录预配到本地环境或 SharePoint Online 环境。 将加载项上传到加载项目录如同将文件上传到文档库一样简单。 有关详细说明,请参阅使用应用程序目录为 SharePoint Online 环境提供自定义业务应用

可以从加载项目录将加载项部署到租户中的一个或多个网站。 由于加载项有权访问租户中的数据,因此只需部署到一个网站即可访问整个租户上的数据。 有关如何从加载项目录部署加载项的说明,请参阅部署自定义加载项

若要下载说明如何跨网站集访问数据的代码示例,请参阅在租户范围的加载项中使用跨域库 (REST)

跨不同安全区域发起调用

跨域库使用托管在加载项页上的 IFrame 中的代理页启用通信。 加载项页和 SharePoint 网站位于不同的安全区域,无法发送授权 cookie。 如果没有授权 cookie 且 IFrame 尝试加载代理页,则会被重定向到 SharePoint 登录页面。 出于安全考虑,不能将 SharePoint 登录页面包含在 IFrame 中。 在这些场景中,库不能加载代理页,并且无法与 SharePoint 通信。

但是,这些方案有一个解决方案。 解决方案是 apphost 模式,它包括将外接程序页面包装在外接程序 Web 中托管的页面中。 最好在使用跨域库的外接程序中使用 apphost 模式,即使没有明显的安全边界也是如此。 有关详细信息,请参阅 在 SharePoint 外接程序中跨不同 Internet Explorer 安全区域使用跨域库

从 SharePoint 托管的加载项中的其他远程主机访问数据

默认情况下,只要具有相应的权限,就允许 SharePoint 承载的外接程序向主机 Web 发起跨域调用。 但是,SharePoint 承载的外接程序也可以在其 AppPrincipalAllowedRemoteHostUrl 属性中指定一个远程主机。 这将使您可以有效地从外接程序 Web 和其他主机发起跨域调用。

若要下载使用跨域库的 SharePoint 托管的加载项,请参阅代码示例:在 SharePoint 托管的加载项中使用跨域库 (REST)

另请参阅