2012年11月7日,星期三

Access end-user data (in the 主机网站) from a SharePoint 2013 app

继续我有关开发SP2013应用程序的系列文章,在本文中,我们’我将研究访问主机Web中的数据所需的特殊方法–表示用户与之互动的常规列表/库 您的应用程序创建的任何内容。这是某些应用程序需要做的,而其他应用程序则不需要。对于一些, 应用程序功能的整个前提 will be working with collaboration data in team sites/My Sites etc., so these techniques will be crucial there. Because there are so many 味道s of SharePoint data access in apps, I’我将花一些时间来解释本文适用于–请随意滚动到代码示例!

虽然本系列文章着重于 SharePoint托管的应用 我在此处描述的某些代码/技术也可能在其他应用程序场景中工作,例如提供商托管的应用程序(例如,该应用程序实现为在另一台服务器上托管的.NET站点)。在撰写本文时(2012年11月),MSDN示例“极其”令人困惑,无法在哪种情况下使用哪种方法–我在下面对此进行了一些讨论,但是基本上,我将在Office 365和“provider-hosted”场景对我来说更加清晰。目前,以下所有示例均已通过测试 an on-premise SharePoint托管 app which accesses data in the 主机网站.

这里’s本系列文章的目录不断变化:

  1. SharePoint 2013应用–架构,功能和UX注意事项
  2. 入门–在SharePoint应用程序中创建列表,内容类型,字段等(配置)
  3. Working with data in the 应用程式网页, 和 why you should
  4. Access end-user data (in the 主机网站) from a SharePoint 2013 app [本文]
  5. 将SharePoint 2013应用推广到企业-租户范围和PowerShell安装
  6. Azure是新的SharePoint‘_layouts’ directory
  7. “Host web apps” – provisioning files (e.g. master pages) to the 主机网站
  8. “Host web apps” –设置字段和内容类型
  9. Deploying SP2013 提供者托管 apps/Remote Event Receivers to Azure Websites (for Office 365 apps)
  10. 在SharePoint应用程序中使用Web部件

在我们开始研究之前,如果您’不确定我的意思“host web” 和 “app web”, then Working with the 应用程式网页, 和 why you should 详细介绍,但是这里’s a quick reminder:–

  • 主机网站 -的regular team site where our users access their documents etc., 和 where the app is installed
  • 应用程式网页 -的“isolated”子网站,创建该子网站以容纳应用程序创建的任何SharePoint工件(例如页面,列表等),并且通常在用户进入应用程序时将其定向到该子站点 

Flavors of 远程 data access in apps

当您浏览MSDN示例和此类文章时,我的建议是了解哪些“flavor” of task you’重新尝试完成,以及本文讨论的内容。排列令人难以置信,并导致在理解应用程序的过程中产生很多困惑–这里有些例子:

应用类型

远程数据访问

SharePoint托管 Page in 应用程式网页 accessing 主机网站 data using the cross-domain library 和 REST
SharePoint托管 Page in 应用程式网页 accessing 主机网站 data using JavaScript CSOM
自动托管(Azure)/
提供者托管/O365 app
Remote HTML page which uses cross-domain library to access data in 应用程式网页
自动托管(Azure)/
提供者托管/O365 app
Remote HTML page which JavaScript CSOM to access data in 应用程式网页
自动托管(Azure)/
提供者托管/O365 app
使用跨域库访问主机Web中的数据的远程HTML页面(也许– haven’还没看完)
自动托管(Azure)/
提供者托管/O365 app
远程HTML页面可制作标准AJAX请求以托管Web或其他SharePoint网站,并将OAuth令牌添加到‘Authorization’ header
自动托管(Azure)/
提供者托管/O365 app
远程.NET代码(例如Web服务,预定任务等),该代码使用.NET CSOM请求宿主网站或其他SharePoint网站中的数据(使用TokenHelper类传递OAuth令牌)

我有想念吗?请注意,这些只是访问SharePoint列表数据–例如,在Azure /其他位置访问SQL数据还有更多方案。

斜体 那些就是我’m focusing on in this article. Interestingly, I notice the 3rd 和 4th 味道s are covered in some MSDN samples (see 这里这里 分别),所以技术我 ’m writing about 这里 should have broader usage than just my first two 味道s. Confusingly however, those samples only appear to work when the 提供者托管 远程 site is running on http://本地主机,并且没有注释可以解释这一点 –如果将其配置为真实的,可部署的IIS网站,则代码将停止工作(并且开始需要其他东西,例如X-UA-Compatible元标记)。我的猜测是浏览器/ JavaScript安全检查’用本地主机执行(它知道不是’t 真实ly 远程), 和 when this isn’t the case then “high-trust”本地方案需要配置应用程序(这涉及使用Register-SPAppPrincipal等指定应用程序客户端ID)。一世’以后再确认。

一个值得注意的遗漏是“accessing data in the 应用程式网页 from the 主机网站” (即相反方向)–例如,团队站点中的Web部件“reaches into”读取/写入一些数据的应用程序。该场景对我来说有点有用,但是我怀疑该应用程序模型不允许这样做。 我的测试表明这是不可能的,并且有适当的阻止机制– you’ll get a "抱歉,此网站尚未与您共享" error message when you execute your 远程 call.

何时何地–使用JavaScript跨域库(SP.RequestExecutor)

如果你’在使用SharePoint客户端对象模型(CSOM)完成开发之前,您可能会问为什么我们需要特殊的JavaScript库才能从应用程序访问某些数据–当然,我们只是将URL传递给SP.ClientContext对象,对吗?好吧,这不’总是工作。答案是跨站点脚本–或更正确地说,浏览器必须提供的保护 避免 跨站点脚本攻击。这集中在‘same-origin’JavaScript /网络浏览器使用的策略,它指定一些客户端代码 只能访问同一URL域中的数据 –否则,网站可能会有效地相互攻击,互联网将变得混乱。

但是,在许多应用程序SharePoint应用程序场景中,您可能想要访问’t与您的代码位于同一URL域(通常是网页中的JavaScript)。例如,如果您’遵循最佳做法,您的SharePoint应用程序在单独的URL域上运行(例如 http:// [identifier] .cob-apps.dev 相比 http://team.cob.dev 在我的测试VM中),那么这对您来说就是一个问题。

跨域库旨在简化这些JavaScript方案–尤其是无需使用OAuth令牌。

这个怎么运作

因此,如果通常禁止JavaScript请求在URL域之间进行交谈,那么跨域库如何做到这一点?简而言之,所使用的技术是在主机网络中的页面上动态创建IFrame–默认情况下,这是一个开箱即用的应用程序页面,名为AppWebProxy.aspx,尽管它’也可以使用自己的。这实际上使“remote”通过在主机Web URL(例如, http://team.cob.dev/_layouts/15/AppWebProxy.aspx  –与SharePoint列表对话的JavaScript代码在此处执行,然后从此处执行’代码可能会读取IFrame中显示的值。尽管自从Microsoft使用Script#生成JavaScript(就像CSOM的其余部分一样)以来,JavaScript就有些模糊了,但是您可以在SP.RequestExecutor.js中看到一些实现细节:

SP.RequestExecutor 

上下文-我如何在自己中使用所有这些“learner” time—tracking app

在开始示例之前,对于那些依稀关注本文系列的人们’s how I’我在虚拟应用程序中使用跨域库’米大厦。我的应用主要存储它’在应用程序网络中的数据,但仅出于娱乐目的,主机网络中有一个列表,用于存储“target hours”用户必须记录该周:

UtililsationTargetsList

我使用该库查询当前用户的此列表,然后在我的应用程序中显示该列表:

TimeTracking_SummaryUnderTarget 

例子

1. Get title, URL or other properties of 主机网站 (_api/REST):

在第一个示例中,我’我们将尝试介绍使用REST服务的一些基础知识。 (注:这是JavaScript中的异步/ AJAX编程-如果您尚未’在做完这些之前,您可能需要另一个参考才能开始。查看 使用JavaScript Client OM和jQuery处理列表)。 本质上,我们构建了一个REST URL(可以在浏览器地址栏中进行测试),然后指定我们是否希望将数据返回为JSON或XML。大多数样本倾向于使用JSON,但是我’稍后将展示如何获取XML-尽管JSON通常很有意义,因为它与JavaScript / jQuery很好地兼容。所以我们 使用一些样板异步代码从服务返回的字符串中获取JSON对象。在研究这些示例时,需要注意的关键是我们要求的数据将始终位于 jsonObject.d 属性–当您看到更多这些示例时,这将很有意义,但是’了解何时您至关重要’重新编写自己的代码:

var hostweburl;
var appweburl;
 
function sharePointReady() {
    // retrieve passed 应用程式网页/host web URLs..
    hostweburl = decodeURIComponent($.getUrlVar("SPHostUrl")); 
    appweburl = decodeURIComponent($.getUrlVar("SPAppWebUrl")); 
 
    loadDependentScripts();
}
 
function loadDependentScripts() {
    var scriptbase = hostweburl + "/_layouts/15/";
 
    // Load the js files 和 continue to the successHandler
    $ .getScript(scriptbase +"SP.Runtime.js",
        function () {
            $ .getScript(scriptbase +"SP.js",
                function () { $ .getScript(scriptbase +"SP.RequestExecutor.js", getAllHostWebPropsUsingREST); }
                );
        }
    );
}
 
function getAllHostWebPropsUsingREST() {
    var executor;
 
    // although we're fetching data from the 主机网站, SP.RequestExecutor gets initialized with the 应用程式网页 URL..
    executor = new SP.RequestExecutor(appweburl);
    executor.executeAsync(
        {
            url:
                appweburl +
                "/_api/SP.AppContextSite(@target)/web/?@target='"+ hostweburl +"'",
            method: "GET",
           标头s: { "Accept": "application / json; odata =详细" },
            success: onGetAllHostWebPropsUsingRESTSuccess,
            error: onGetAllHostWebPropsUsingRESTFail
        }
    );
}
 
function onGetAllHostWebPropsUsingRESTSuccess(data) {
    var jsonObject = JSON.parse(data.body);
    // note that 这里 our jsonObject.d object (representing the web) has ALL properties because 
    // we did not select specific ones in our REST URL. However, we're just displaying the web title 这里..
    $('#hostWebTitle')。text(jsonObject.d.Title);
}
 
function onGetAllHostWebPropsUsingRESTFail(data, errorCode, errorMessage) {
    alert('Failed to get host site. Error:' + errorMessage);
}
 
// jQuery plugin for fetching querystring parameters..
jQuery.extend({
    getUrlVars: function () {
        var vars = [], hash;
        var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
        for (var i = 0; i < hashes.length; i++) {
            hash = hashes[i].split('=');
            vars.push(hash[0]);
            vars[hash[0]] = hash[1];
        }
        return vars;
    },
    getUrlVar: function (name) {
        return jQuery.getUrlVars()[name];
    }
});

因此,鉴于我们的REST请求要求提供托管网站及其所有内容’的属性,如果我使用IE Dev Tools脚本调试器在 jsonObject.d 属性,我看到了一些可识别的网络属性:

分析JSONObject_WebProps

在上面的代码示例中,您可以看到我要求“Title” 属性and put it in a DIV on the page with the following line:

$('#hostWebTitle')。text(jsonObject.d.Title);

毫不奇怪,这是屏幕上的样子:

WebProps_Output

只需注意,如果您只需要一个(或几个)网络资源,它就可以’仅向服务器询问这些属性的性能和效率要高得多(因为将较少的数据下载到客户端)。为此,REST URL应该更改为以下内容(仅在此处询问标题):

appweburl +"/_api/SP.AppContextSite(@target)/web/标题?@ target ='" + hostweburl + "'"

2. Querying for lists in the 主机网站 (_api/REST)

在此示例中,我们要求主机网络中的所有列表–对于每一项,我们列出标题和当前项数。 REST部分的更改很简单(我们使用“/ web /列表"作为网址),但是一旦我们有了数据,就需要考虑一下– we have a 采集 此处的项目数,因此需要进行一些迭代。因此,在我的成功经理中’m using jQuery’的每个方法都有一个使用Title和ItemCount属性的回调。

请注意,这是一个简化的示例:

// omitted for brevity: variable declarations, setup code to load dependent scripts, get hostweburl from querystring etc. (see previous example for this)
function getHostWebListsUsingREST() {
    var executor;
 
    // although we're fetching data from the 主机网站, SP.RequestExecutor gets initialized with the 应用程式网页 URL..
    executor = new SP.RequestExecutor(appweburl);
    executor.executeAsync(
        {
            url:
                appweburl +
                "/_api/SP.AppContextSite(@target)/web/lists/?@target='"+ hostweburl +"'",
            method: "GET",
           标头s: { "Accept": "application / json; odata =详细" },
            success: onGetHostWebListsUsingRESTSuccess,
            error: onGetHostWebListsUsingRESTFail
        }
    );
}
 
function onGetHostWebListsUsingRESTSuccess(data) {
    var jsonObject = JSON.parse(data.body);
    var lists = jsonObject.d.results;
    var listsHtml = $.each(lists, function (index, list) {
        $('#lists')。append(list.Title + "(" + list.ItemCount + ")<br />");
    });
}
 
function onGetHostWebListsUsingRESTFail(data, errorCode, errorMessage) {
    alert('Failed to get host site. Error:' + errorMessage);
}

如果查看JavaScript调试器中返回的数据,则可以看到我的集合:

分析JSONObject

..并且在页面上,我看到我的列表及其各自的项目计数:

ListsAndItemCounts_Output

3. Querying for specific items in a specific list in the 主机网站 (_api/REST)

这个样品是我的‘real’我的时间跟踪应用中的代码-用于屏幕快照中,显示了我之前显示的“目标时间”。在这里,我进行常规设置工作以确保首先加载脚本。然后,在这种情况下,我’m查询属于当前用户的记录–因此,我需要一个初始CSOM请求来确定这是谁,然后,一旦有了用户名,便可以继续进行主查询。由于此两步过程,我将显示此示例的完整代码。与前面的示例一样,然后我使用 SP.RequestExecutor 调用主机网络并获取数据-有效地,我使用要查询的列表/列的详细信息构建了一个REST URL,并将其传递给 RequestExecutor.executeAsync() –然后,我的数据以JSON格式返回:

var context;
var user;
var hostweburl;
var appweburl;
 
var utilTargetsList = "Utilisation%20targets";
var utilTargetsListFriendlyName = "Utilisation targets";
 
var userName = "Chris OBrien";
var fetchedUserName;
var targetHoursResult;
 
// This function is executed after the DOM is ready 和 SharePoint scripts are loaded
// Place any code you want to run when Default.aspx is loaded in this function
// 的 code creates a context object which is needed to use the SharePoint object model
function sharePointReady() {
    // retrieve passed 应用程式网页/host web URLs..
    hostweburl = decodeURIComponent($.getUrlVar("SPHostUrl")); 
    appweburl = decodeURIComponent($.getUrlVar("SPAppWebUrl")); 
    loadDependentScripts();
}
 
function loadDependentScripts() {
    var scriptbase = hostweburl + "/_layouts/15/";
 
    // Load the js files 和 continue to the successHandler
    $ .getScript(scriptbase +"SP.Runtime.js",
        function () {
            $ .getScript(scriptbase +"SP.js",
                function () { $ .getScript(scriptbase +"SP.RequestExecutor.js", runCode); }
                );
        }
    );
}
 
function runCode() {
    loadUser();
}
 
function loadUser() {
    var ctx = new SP.ClientContext.get_current();
    user = ctx.get_web().get_currentUser();
 
    ctx.load(user);
    ctx.executeQueryAsync(onGetUserNameSuccess, onGetUserNameFail);
}
 
function onGetUserNameSuccess() {
    fetchedUserName = user.get_title();
 
    // now we have a username, we can execute our main query..
    fetchTargetHoursFromHostWebUsingREST();
}
 
// This function is executed if the above OM call fails
function onGetUserNameFail(sender, args) {
    alert('Failed to get user name. Error:' + args.get_message());
}
 
function fetchTargetHoursFromHostWebUsingREST() {
    var executor;
 
    // although we're fetching data from the 主机网站, SP.RequestExecutor gets initialized with the 应用程式网页 URL..
    executor = new SP.RequestExecutor(appweburl);
    executor.executeAsync(
        {
            url:
                appweburl +
                "/_api/SP.AppContextSite(@target)/web/lists/getbytitle('" + utilTargetsList + "')/items?@target='" +
                hostweburl + "'&$filter=Title eq '" + userName + "'&$select=Target",
            method: "GET",
           标头s: { "Accept": "application / json; odata =详细" },
            success: onGetTargetHoursByRESTSuccess,
            error: onGetTargetHoursByRESTFail
        }
    );
}
 
function onGetTargetHoursByRESTSuccess(data) {
    var jsonObject = JSON.parse(data.body);
    $('#targetHours')。text(jsonObject.d.results[0].Target);
}
 
function onGetTargetHoursByRESTFail(data, errorCode, errorMessage) {
    alert('Failed to get host site. Error:' + errorMessage);
}

如果你 prefer to use the JavaScript CSOM instead of REST, you can do that – let’s look 在 a sample.

4. Querying for specific items in a specific list in the 主机网站 (JavaScript CSOM)

[注意–不像其他技术,我怀疑这是赢了’t work in other app scenarios such as a 提供者托管 app calling back to the 应用程式网页/host web. To repeat, I’m 真实ly focusing about a page in a SharePoint托管 app 这里.] 因此,前面的示例都使用了REST,但是让’s说我们要改用JavaScript客户端对象模型。在这种情况下,无需使用跨域库–但是有一个技巧可以查询宿主网站而不是应用程序网站。请记住,应用程序网络是默认上下文,因为’运行此代码的位置。要使用CSOM访问主机网络, 我需要创建一个SP.AppContextSite实例,并将其传递给应用程序和宿主网站的详细信息。当你’ll know if you’ve used the CSOM in ‘classic’场景中,数据是’此处以JSON返回,但在我可以枚举的集合中:

// omitted for brevity: variable declarations, setup code to load dependent scripts, get hostweburl from querystring etc. (see previous example for this) 
 
// start of core methods to fetch 主机网站 data using JavaScript CSOM..
function fetchTargetHoursFromHostWebCSOM() {
    appWebContext = new SP.ClientContext.get_current();
    var hostWebContext = new SP.AppContextSite(appWebContext, hostweburl);
 
    var targetHoursList = hostWebContext.get_web().get_lists().getByTitle(utilTargetsListFriendlyName);
    var query = new SP.CamlQuery();
    query.set_viewXml("<View><Query><Where><Contains><FieldRef Name='Title'/><Value Type='Text'>" + userName + "</Value></Contains></Where></Query></View>");
 
    targetHoursResult = targetHoursList.getItems(query);
    appWebContext.load(targetHoursResult);
    appWebContext.executeQueryAsync(onGetTargetHoursByCSOMSuccess, onGetTargetHoursByCSOMFail);
}
 
function onGetTargetHoursByCSOMSuccess(data) {
    var listEnumerator = targetHoursResult.getEnumerator();
    while (listEnumerator.moveNext()) {
        $('#targetHours')。text(listEnumerator.get_current().get_item("Target")); 
    }
}
 
function onGetTargetHoursByCSOMFail(data, errorCode, errorMessage) {
    alert('Failed to get host site by CSOM. Error:' + errorMessage);
}

NOTE:- 如果你 need sample JavaScript for other tasks (e.g. adding an item to a list, deleting a list etc.) - check out 如何:在SharePoint 2013中使用JavaScript库代码完成基本操作 –这涉及许多这样的情况。

REST _api的使用技巧

确定要使用的REST URL

如果你’重新选择使用REST而不是CSOM,那么我能给您的最好提示是制定REST URL 首先使用浏览器,然后将该网址与您的代码集成在一起(例如,处理应用程序网络/托管网络事物),作为第二步。我的意思是,只需在浏览器地址栏中输入并检查返回的数据-默认情况下,它将以XML格式返回,但是您 ’仍然会看到哪些对象/属性返回。

所以在这里’s me checking that I can fetch a single 属性of the web with a certain URL:

BrowserRequest_WebTitle
..和这里’s me checking that I’将使用不同的URL获取网络中的所有列表:

BrowserRequest_ListsInWeb
您’使用这种方法可以更快地反馈信息,而不必等待应用程序针对每次更改进行部署。

返回XML而不是JSON

如果由于某种原因 ’d倾向于使用XML而不是JSON,那么您可以指定要返回的XML。使用Accept标头执行此操作–只需交换此行:

标头:{"Accept": "application / json; odata =详细" }

..对于:

标头:{"Accept": "application/xml" }

您’然后,需要相应地调整成功处理程序中的代码。

Permissions required to access 主机网站 data

对于SharePoint 2013应用程序,安全性,身份验证,授权和权限级别都是相当重要的主题。但是,与本文相关的要点是,默认情况下,访问主机Web中的数据的代码(如上述示例)将不起作用 until the app is granted permissions to data in the 主机网站。考虑是否每个应用程序都可以访问每个用户’的数据,即使是只读的–好吧,整个事情会很混乱。

以下项目符号试图总结此处的情况:

  • 就像用户一样,应用程序也拥有它’可以授予权限的自己的身份–这被称为“app principal”
  • 如果应用程序具有应用程序网络,则应用程序主体将自动对此区域具有“完全控制”权限–在这里读/写数据不需要任何额外的操作
  • 相比之下,该应用仅“basic permissions”到宿主网站或任何其他SharePoint网站–您可以获得一些核心属性,例如网络标题,但仅此而已
  • 应用开发人员必须使用“permission request”指定应用程序应能够在主机网络中使用哪些数据
  • 这是在AppManifest.xml文件中完成的–Visual Studio甚至有一个设计师来帮助正确设置XML
  • 然后,安装该应用程序的人将看到需要哪些权限,并且必须同意此权限才能安装该应用程序

在相关说明中,如果您’奇怪的是,应用程序彼此隔绝,无法互相读写’s data.

我提到Visual Studio有一个设计师可以帮助您–这是突出显示的“应用程序权限”部分:

AppManifestDesigner_PermissionRequests

尽管您可能主要在设计器模式下使用AppManifest.xml,但为清楚起见,让我们’现在考虑“Code View”模式。在上面的示例和时间跟踪应用中,我’在建筑物中,我想读取整个主机网络中的数据。在这种情况下,我需要这样的权限请求:

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

为了完整起见,这是我完整的AppManifest.xml,其中包含以下内容:

<?xml version="1.0" encoding="utf-8" ?>
<App xmlns="http://schemas.microsoft.com/sharepoint/2012/app/manifest"
     Name="COBSharePointAppsTimeTracking"
     ProductID="{5fec590d-3fd3-410d-82d9-77d13c2d3bdf}"
     Version="1.0.0.0"
     SharePointMinVersion="15.0.0.0"
>
  <Properties>
    <标题>COB Time Tracking app</标题>
    <StartPage>~appWebUrl/Pages/Default.aspx?{StandardTokens}</StartPage>
  </Properties>
 
  <!-- start of permission-related elements -->  
  <AppPrincipal>
    <Internal />
  </AppPrincipal>
 
  <AppPermissionRequests>
    <AppPermissionRequest Scope="http://sharepoint/content/sitecollection/web" Right="Read" />
  </AppPermissionRequests>
  <!-- end of permission-related elements -->  
</App>

在许多方面,要求读取Web中的所有数据是“a big ask”对于一个应用程序。将其限制为特定列表可能更合适,在这种情况下,声明应类似于:

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

请注意,我可以’指定一个特定的列表。一世’我猜微软是通过这种方式设计事物的,因为列表是动态的(并且在创建时分配了唯一的标识符)–因此,这里发生的是,安装应用程序的管理员从站点中所有列表的下拉列表中选择*哪个*应用程序有权访问的列表:

AppInstall_ListPermissionRequest

权限请求远远超出了SharePoint网站中的数据。作为应用程序开发人员,您还可以指定需要允许该应用程序使用核心SharePoint服务,例如搜索,分类法,用户配置文件等。同样’可以指定“能力前提” 和 “功能先决条件 ”,以确保该应用程序仅在实际运行这些服务的环境中使用。

So, as you might imagine there are many permutations of 许可请求s –有关更多详细信息,请参见 SharePoint 2013中的应用程序权限.

概要

某些应用需要访问“real”应用程序外部的最终用户数据’s storage area (app web), though the must request permissions (and be granted them 在 install time) to do this. Several techniques can be used, including REST 和 the JavaScript CSOM (also known as JSOM). This article presents some code samples, as well trying to be clear on which of the many 远程 data scenarios they apply to.

将来我’ll revisit the topic of using the JavaScript cross-domain library in 远程 提供者托管 apps.

下次:在SharePoint应用程序中使用Web部件

13条评论:

未知说过...

嘿克里斯,
谢谢您再次提供优质博文。

据我了解,跨域库获取跨域主机Web数据的替代方法可以使用SharePoint代理(SP.WebProxy和.SP.WebRequestInfo),在app.manifest中指定RemoteEndpointUrl。
您是否了解这两种方法之间的利弊信息(在SharePoint代理情况下,'我需要在应用清单中指定主机网址。

谢谢,
哈雷尔

克里斯·奥'Brien说过...

嘿,哈雷

我没有't yet used SP.WebProxy/SP.RequestInfo, but my understanding was that these were for calling 远程 services which don'与SharePoint没有任何关系。因此,例如,如果我想通过应用程序中的JavaScript在Internet上调用一些任意服务,'d需要使用这些对象。

如果您有任何不同之处,请告诉我!

干杯,

克里斯。

未知说过...

谢谢克里斯。
是的,同意,Web代理主要用于出站呼叫,而跨域或Oauth用于入站呼叫,但是我仍然能够使用Web代理检索入站方案的一些数据,并且想知道两者之间的显着差异。
将继续玩这个,看看我是否能找到任何东西:)

未知说过...

感谢您的研究,以帮助我们更清楚地了解此主题...

匿名 said...

非常感谢您提供这些有用的博客文章!

我想为启用了匿名访问的面向公众的网站编写一个实现以下功能的应用程序:

1.让用户输入一些数据(例如在调查中)
2.将数据存储到主机/应用程序网络列表中

可以保护该列表,以使匿名访问无法检索任何条目吗?
AFAIK可以仅使用客户端JSOM代码来检索那些列表项吗?

知道上述方法是否可行,以及如何实现该功能将非常有帮助。

尔凡·马利克(Irfan Malik)说过...

克里斯,你好
我想您现在可能已经知道了这一点,即在调试云托管应用程序时,它将始终在本地主机中打开。
您 need to Deploy from visual studio to run on Azure instead.

-尔凡·马利克(Irfan Malik)

克里斯·奥'Brien说过...

@Irfan,

确实。我最近与VS / SharePoint工具人员进行了交谈,他们正在致力于在云中启用更多调试方案。但是就目前而言,在本地(在http:// localhost上)运行确实是's needed for debugging a 提供者托管 app.

干杯,

克里斯。

道格说过...

嘿克里斯,
我在做最简单的事情时遇到了极大的困难。我在sharepoint托管应用中有一个简单的aspx页面,在该页面上有一个html按钮,该按钮后面有一个javascript方法。当我单击按钮时,将执行javascript,并在按钮代码中希望访问共享点(2013)列表。

我被困住了,因为我似乎无法得到"hook"进入SP js脚本。这里's my code:

var hostWebUrl =解码URIComponent(getQueryStringParameter("SPHostUrl"));

var scriptbase = hostWebUrl +"/_layouts/15/";

$ .getScript(scriptbase +"SP.Runtime.js",
函数(){
$ .getScript(scriptbase +"SP.js",
函数(){$ .getScript(scriptbase +"SP.RequestExecutor.js", runcode); }
);
}
);

var context = new SP.ClientContext.get_current();


aspx页面作为iFRAME内的Web部件添加到共享点页面。

单击按钮后,在运行时出现错误"SP.ClientContext.get_current()"告诉我找不到SP。

什么's missing 这里?

谢谢。
道格

道格说过...

克里斯,我回答了我自己的问题。我在aspx页面中没有对MicrosoftAjax.js的引用。一旦我确定需要添加,就可以了。

< s.c.r.i.p.t. type="text/javascript" src="//ajax.aspnetcdn.com/ajax/4.0/1/MicrosoftAjax.js">

感谢您的光临。

克里斯·奥'Brien说过...

@道格

很好,很高兴您能接受排序-我还在考虑那个:)

I've还注意到其他症状/错误消息,这些症状/错误消息实质上指向没有引用MicrosoftAjax.js。一世've got a '故障排除应用'帖子来谈论这样的一些观察。

干杯,

克里斯。

道格说过...

克里斯,这是一个更困难的问题:这次在SharePoint托管应用程序中,如何访问主机* Page *上存在的* Page *(即DOM)元素(即托管应用程序的页面上的DOM元素)部分,而不是主机* Web *中的SharePoint工件?当我编写通过功能区按钮单击运行的SP托管应用程序时,我能够做到这一点,但是当我编写在Web /应用程序部件中加载页面的SP托管应用程序时,宿主* Page *元素不会对我可见。我可以't弄清楚如何访问这些项目。如果让页面完全加载,然后按F12键查看开发人员视图,则可以轻松找到DOM项,但是可以'似乎在document.ready过程中在SharePoint托管应用中引用了它们。

有什么想法吗?
谢谢。

未知说过...

克里斯,您好,谢谢分享。

但在我的情况下,我'我没有使用SP应用程序,如果我只是使用MVC网站,并且通过JSOM我得到了拒绝访问错误。

因此,可以在没有SP APP上下文的情况下通过JSOM访问SP数据吗?

谢谢,
Z Z

亚当·托斯说过...

嘿道格,

如果你 want to access the host page 和 its DOM information, you'我将需要使用postMessage API进行跨域通信。由于AppPart是与主机页面托管在不同域上的iframe,因此它受浏览器的约束'的跨域安全限制,并且无法访问或访问主机页面's elements.

那'一件好事,因为你不会'希望某些第三方应用程序部分在您不知情或未明确同意的情况下从其页面上抓取敏感数据。

我写了一个关于使用postMessage api选择共享某些主机页面信息的方法:

http://www.lifeonplanetgroove.com/use-postmessage-shim-give-sharepoint-client-app-parts-information