在 SharePoint 中使用 Web 代理查询远程服务

在构建 SharePoint 外接程序时,您通常必须包含来自各个源的数据。 出于安全原因,我们制定了防止跨域通信的阻止机制。 当您使用 Web 代理时,您的外接程序中的网页可访问远程域和 SharePoint 域中的数据。

作为开发者,可以使用在客户端 API 中公开的 Web 代理,如 JavaScript 和 .NET 客户端对象模型。 使用 Web 代理时,将向 SharePoint 发出初始请求。 反过来,SharePoint 向指定终结点请求数据,并将该请求转发回你的页面。

如果你希望通信出现在服务器级别,请使用 Web 代理。 有关详细信息,请参阅 SharePoint 加载项的安全数据访问和客户端对象模型

SharePoint Web 代理是代码和外部数据源之间的中间人

用于“你的代码”、“SharePoint Web 代理”和“数据源”的符号,它们可以显示通过 Web 代理的数据请求。

使用本文中的示例的先决条件

若要按照此示例中的步骤操作,则需要:

使用 Web 代理前要了解的核心概念

下表列出了一些有用的文章,可帮助你了解 SharePoint 加载项的跨域方案中涉及的概念。

文章标题 说明
SharePoint 外接程序 了解 SharePoint 中新的外接程序模型,可以利用此模型来创建对最终用户来说是小型且易于使用的解决方案的外接程序。
SharePoint 外接程序的安全数据访问和客户端对象模型 了解 SharePoint 外接程序中的数据访问选项。本文提供了有关在使用外接程序中的数据时必须从中进行选择的高级别备用项的指南。
SharePoint 中的主机 Web、外接程序 Web 和 SharePoint 组件 了解主机 Web 和外接程序 Web 之间的区别。 了解 SharePoint 外接程序中可以包括哪些 SharePoint 组件、将哪些组件部署到主机 Web、将哪些组件部署到外接程序 Web 以及如何将外接程序 Web 部署到隔离域中。
客户端跨域安全性 了解跨域威胁和用例以及跨源请求的安全准则,并权衡开发人员增强从在浏览器中运行的 Web 应用程序进行的跨域访问的风险。

代码示例:使用 Web 代理访问远程服务中的数据

若要从远程服务中读取数据,必须执行以下操作:

  1. 创建 SharePoint 外接程序项目。

  2. 修改"Default.aspx"页以使用 Web 代理查询远程服务。

  3. 修改加载项清单以允许与远程域的通信。

下图显示的浏览器窗口包含 SharePoint 网页远程服务中的数据。

包含远程服务中的数据的 SharePoint 网页

包含远程服务中的数据的 SharePoint 页面

创建 SharePoint 加载项项目

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

  2. 使用 SharePoint 加载项模板创建新的项目。

    下图显示了“SharePoint 加载项”模板在 2015 中的位置,具体位于“模板”>“Visual C#”>“Office/SharePoint”>“Office 加载项”下。

    SharePoint 加载项 Visual Studio 模板

    SharePoint 相关应用程序 Visual Studio 模板


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

  4. 选择"Sharepoint 托管"作为您的外接程序的托管选项。

使用 JavaScript 对象模型修改 Default.aspx 页以使用 Web 代理

  1. 双击"页面"文件夹中的"Default.aspx"页。

  2. 复制以下标记并将其粘贴到页面的 PlaceHolderMain 内容标记中。 该标记将执行以下任务:

    • 为远程数据提供占位符。

    • 引用 SharePoint JavaScript 文件。

    • 准备包含 WebRequestInfo 对象的请求。

    • 准备请求 Accept 标头来以 JavaScript 对象表示法 (JSON) 格式指定响应。

    • 发出对远程终结点的调用。

    • 处理成功完成的操作,并在 SharePoint 网页中呈现远程数据。

    • 处理任何错误,并在 SharePoint 网页中呈现错误消息。

    Categories from the Northwind database exposed as an OData service: 
    
    <!-- Placeholder for the remote content -->
    <span id="categories"></span>
    
    <!-- Add references to the JavaScript libraries. -->
    <script 
        type="text/javascript" 
        src="../_layouts/15/SP.Runtime.js">
    </script>
    <script 
        type="text/javascript" 
        src="../_layouts/15/SP.js">
    </script>
    <script type="text/javascript">
    (function () {
        "use strict";
    
        // Prepare the request to an OData source
        // using the GET verb.
        var context = SP.ClientContext.get_current();
        var request = new SP.WebRequestInfo();
        request.set_url(
            "http://services.odata.org/Northwind/Northwind.svc/Categories"
            );
        request.set_method("GET");
    
        // We need the response formatted as JSON.
        request.set_headers({ "Accept": "application/json;odata=verbose" });
        var response = SP.WebProxy.invoke(context, request);
    
        // Let users know that there is some
        // processing going on.
        document.getElementById("categories").innerHTML =
                    "<P>Loading categories...</P>";
    
        // Set the event handlers and invoke the request.
        context.executeQueryAsync(successHandler, errorHandler);
    
        // Event handler for the success event.
        // Get the totalResults node in the response.
        // Render the value in the placeholder.
        function successHandler() {
    
            // Check for status code == 200
            // Some other status codes, such as 302 redirect
            // do not trigger the errorHandler. 
            if (response.get_statusCode() == 200) {
                var categories;
                var output;
    
                // Load the OData source from the response.
                categories = JSON.parse(response.get_body());
    
                // Extract the CategoryName and Description
                // from each result in the response.
                // Build the output as a list.
                output = "<UL>";
                for (var i = 0; i < categories.d.results.length; i++) {
                    var categoryName;
                    var description;
                    categoryName = categories.d.results[i].CategoryName;
                    description = categories.d.results[i].Description;
                    output += "<LI>" + categoryName + ":&amp;nbsp;" +
                        description + "</LI>";
                }
                output += "</UL>";
    
                document.getElementById("categories").innerHTML = output;
            }
            else {
                var errordesc;
    
                errordesc = "<P>Status code: " +
                    response.get_statusCode() + "<br/>";
                errordesc += response.get_body();
                document.getElementById("categories").innerHTML = errordesc;
            }
        }
    
        // Event handler for the error event.
        // Render the response body in the placeholder.
        // The body includes the error message.
        function errorHandler() {
            document.getElementById("categories").innerHTML =
                response.get_body();
        }
    })();
    </script>
    

(可选)使用 REST 终结点修改 Default.aspx 页以使用 Web 代理

  1. 双击"页面"文件夹中的"Default.aspx"页。

  2. 复制以下标记并将其粘贴到页面的 PlaceHolderMain 内容标记中。 该标记将执行以下任务:

    • 为远程数据提供占位符。

    • 引用 jQuery 库。

    • 准备请求 SP.WebRequest.Invoke 终结点。

    • 使用 SP.WebrequestInfo 对象准备请求的正文。 该对象包括用于以 JavaScript 对象表示法 (JSON) 格式指定响应的 Accept 标头。

    • 发出对远程终结点的调用。

    • 处理成功完成的操作,并在 SharePoint 网页中呈现远程数据。

    • 处理任何错误,并在 SharePoint 网页中呈现错误消息。

    Categories from the Northwind database exposed as an OData service: 
    
    <!-- Placeholder for the remote content -->
    <span id="categories"></span>
    
    <script 
        type="text/javascript" 
        src="//ajax.aspnetcdn.com/ajax/jQuery/jquery-1.8.0.min.js">
    </script>
    
    <script type="text/javascript">
    (function () {
        "use strict";
    
        // The Northwind categories endpoint.
        var url =
            "http://services.odata.org/Northwind/Northwind.svc/Categories";
    
        // Let users know that there is some
        // processing going on.
        document.getElementById("categories").innerHTML =
                    "<P>Loading categories...</P>";
    
        // Issue a POST request to the SP.WebProxy.Invoke endpoint.
        // The body has the information to issue a GET request
        // to the Northwind service.
        $.ajax({
            url: "../_api/SP.WebProxy.invoke",
            type: "POST",
            data: JSON.stringify(
                {
                    "requestInfo": {
                        "__metadata": { "type": "SP.WebRequestInfo" },
                        "Url": url,
                        "Method": "GET",
                        "Headers": {
                            "results": [{
                                "__metadata": { "type": "SP.KeyValue" },
                                "Key": "Accept",
                                "Value": "application/json;odata=verbose",
                                "ValueType": "Edm.String"
                            }]
                        }
                    }
                }),
            headers: {
                "Accept": "application/json;odata=verbose",
                "Content-Type": "application/json;odata=verbose",
                "X-RequestDigest": $("#__REQUESTDIGEST").val()
            },
            success: successHandler,
            error: errorHandler
        });
    
        // Event handler for the success event.
        // Get the totalResults node in the response.
        // Render the value in the placeholder.
        function successHandler(data) {
            // Check for status code == 200
            // Some other status codes, such as 302 redirect,
            // do not trigger the errorHandler. 
            if (data.d.Invoke.StatusCode == 200) {
                var categories;
                var output;
    
                // Load the OData source from the response.
                categories = JSON.parse(data.d.Invoke.Body);
    
                // Extract the CategoryName and Description
                // from each result in the response.
                // Build the output as a list
                output = "<UL>";
                for (var i = 0; i < categories.d.results.length; i++) {
                    var categoryName;
                    var description;
                    categoryName = categories.d.results[i].CategoryName;
                    description = categories.d.results[i].Description;
                    output += "<LI>" + categoryName + ":&amp;nbsp;" +
                        description + "</LI>";
                }
                output += "</UL>";
    
                document.getElementById("categories").innerHTML = output;
            }
            else {
                var errordesc;
    
                errordesc = "<P>Status code: " +
                    data.d.Invoke.StatusCode + "<br/>";
                errordesc += response.get_body();
                document.getElementById("categories").innerHTML = errordesc;
            }
        }
    
        // Event handler for the error event.
        // Render the response body in the placeholder.
        // The 2nd argument includes the error message.
        function errorHandler() {
            document.getElementById("categories").innerHTML =
                arguments[2];
        }
    })();
    </script>
    
    

编辑加载项清单文件

  1. 在“解决方案资源管理器”中,打开“AppManifest.xml”文件的快捷菜单,然后选择“查看代码”

  2. 复制以下 RemoteEndPoints 定义作为 App 节点的子级。

    <RemoteEndpoints>
        <RemoteEndpoint Url=" http://services.odata.org" />
    </RemoteEndpoints>
    

RemoteEndpoint 元素用于指定远程域。 Web 代理将验证发往远程域的请求是否已在外接程序清单中声明。 您最多可在 RemoteEndpoints 元素中创建 20 个条目。 仅考虑授权部分: http://domain:porthttp://domain:port/website 被视为同一终结点。 您可以向同一个域中的很多不同的终结点(只有一个 RemoteEndpoint 定义)发出调用。

生成并运行解决方案

  1. 按 F5 键。

    注意

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

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

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

    下图显示了 SharePoint 网页中的远程数据。

    SharePoint 网页中的远程数据

    包含远程服务中的数据的 SharePoint 页面

解决方案疑难解答

问题 解决方案
在按 F5 键后,Visual Studio 不打开浏览器。 将 SharePoint 加载项项目设置为启动项目。
发生未处理异常“SP 未定义” 确保可在浏览器窗口中访问 SP.RequestExecutor.js 文件。 如果要将本地服务器用作开发环境,则必须禁用 IIS 环回检查。

从 Windows PowerShell 命令提示符运行以下命令:New-ItemProperty HKLM:\System\CurrentControlSet\Control\Lsa -Name "DisableLoopbackCheck" -value "1" -PropertyType dword

警告:不建议在生产环境中禁用 IIS 环回检查。
来自远程终结点的响应的大小超过配置的限制。 Web 代理请求的响应大小不得大于 200 KB。
不支持方案-端口组合。 调用方案-端口组合必须满足以下条件:

方案 - 端口
http - 80
https - 443
http 或 https - 7000-10000

重要说明:出站端口取决于主机防火墙的可用性。 具体而言,只有 http-80 和 https-443 在 SharePoint Online 上可用。

另请参阅