2013年1月6日,星期日

使用REST调用SharePoint搜索(例如从JavaScript或应用程序)

旁注-尽管本文着重于搜索,但我’ve试图略微归纳一下,以便对以下情况也可能有用:

  • 了解使用* any * SharePoint 2013 REST API的模式(例如社交,查询列表/库等)–用于REST调用的URL是会改变的主要部分
  • 看到(广泛地)如何处理以两种可用格式(JSON或XML)格式化的REST API结果
  • 了解如何从中调用REST API SharePoint托管的应用程序(JavaScript)和云应用程序(提供商托管或自动托管)– C#)
  • 简要讨论使用CSOM调用搜索而不是REST

通常’在应用程序中利用SharePoint服务(例如搜索,用户配置文件,托管元数据等)非常有用。具体来说,以搜寻为例’我最近一直在给我‘learner’时间跟踪应用,该应用会显示一个‘Related content’ bar with links to documents and other things which relate to completing 时间表:

主页3

相关内容2

我的搜索查询有些简单化(我’m当前仅搜索包含该词的任何文档和页面‘timesheets’), but it’很容易想到,经过一些改进,我们只能返回高度相关的项目。在现实生活中,一种不错的方法是具有可配置的值,以允许每个客户端(安装应用程序的用户)编辑/存储最适合他们的搜索查询。无论如何,我认为要点是搜索可以是有价值的‘bridge’在应用程序和SharePoint环境的其余部分之间。在我的示例中,请考虑:

  • 作为应用程序供应商,我不知道您的组织是什么’的时间表政策/指南是
  • If 我可以 bring in some of your content around this, users will have more success with my app, and that’s good for everyone

在实施方面,我’现在,我将讨论通过REST访问搜索的基础知识,以及如何从应用程序执行此操作。

大致来说,该过程是:

  1. 识别(并测试)要使用的REST URL
  2. 必要时处理应用程序身份验证(即您’从SharePoint外部重新调用,例如云应用)
  3. 向您的应用添加权限请求,以便成功进行使用搜索的请求
  4. 开发成功处理程序代码以处理结果

通过REST访问搜索(_api)

REST的优点在于’只是一个URL,您可以在编写单行代码之前在浏览器中对其进行测试/优化。在SharePoint 2013中,这样的URL采用以下格式:

[现场]/ _api /[SP API名称] / [操作名称]?[参数]

因此,在搜索的情况下,可以使用以下命令执行基本查询:

[现场]/ _api / search / 询问?querytext =“时间表”

如果在浏览器地址栏中输入了这样的URL,则应该看到这样的XML响应– you’ll find some 有趣 properties, like those I’ve在图像中突出显示:

SearchRESTCallResults

SearchRESTCall_点击突出显示摘要

常用操作

到目前为止,我们’我刚刚看过执行基本查询,其中指定了查询文本。这是您可能要执行的其他一些常见操作:

运作方式

样本REST URL

使用搜索结果源(即范围) / _api / search / 询问?querytext ='搜索词'&sourceid ='B09A7990-05EA-4AF9-81EF-EDFAB16C4E31' (此示例是搜索‘People’ result source)
指定开始行(即在分页中) / _api / search / 询问?querytext ='搜索词'&startrow=11
指定要返回的结果数 / _api / search / 询问?querytext ='搜索词'&startrow=11&rowlimit=10 (但注释10是默认值)
指定特定(托管)属性以返回 / _api / search / 询问?querytext ='搜索词'&selectproperties ='作者,路径,标题,网址'

For a more comprehensive reference, the best source 我可以 find is the SharePoint 2013搜索REST API 张贴在‘Search Space’ blog by ‘nadeemis’ on MSDN –感谢作者。

处理数据

与其他SharePoint REST操作一样,您可以选择以XML或JSON格式存储数据–如果您使用JavaScript,则JSON和JSON可能更可取。当我’之前已经提到过,这是通过“Accept” header –指定以下之一:

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

您处理结果的代码将需要适当处理JSON / XML。无论哪种情况,您’通常我会做一些深入的工作以获得有价值的数据–例如,主要结果行位于名为 jsonObject.d.query.PrimaryQueryResult.RelevantResults.Table.Rows.results (其中jsonObject是从JSON字符串解析的对象)。当你’重新遍历结果,如果您想要该商品的网址,则可以从索引6的单元格中获取该网址, result.Cells.results [6]。值. 标题位于索引3,因此 result.Cells.results [3]。值. 这些基本上只是您使用JavaScript调试器或以下代码示例中的工作而已。

从JavaScript对象模型(JSOM)调用搜索REST服务

在此示例中,我有一个网页,其中有一个DIV等待输出搜索结果:

<div id =“ related-content-results”></div>

如果我’m in a page running on SharePoint (including a SharePoint-hosted app), 我可以 use SP.RequestExecutor (例如,与jQuery.get()或jQuery.getJson()相对),这通过简化我的意思简化了事情’不必担心将OAuth标头添加到我的请求中。因此,此代码如下所示:

** N.B.我较新的代码示例未在RSS阅读器中显示- 点击这里查看全文 **

请注意以下几点:

  • 我们的网址’重新使用的是应用网站,而不需要以某种方式引用宿主网站–这对搜索很好(但不是’t,例如用于访问主机网络中的数据)
  • 我们需要一些东西来遍历JSON中的结果项– jQuery’s 每() 操作员是一个不错的选择
  • 有一些‘magic numbers’在处理JSON结果以获取值时 result.Cells.results [6] 获取该项目的网址。那’只是SharePoint返回的数据结构的反映,而您’如果你会看到相同的’重新使用XML

从.NET CSOM中的云应用程序(提供者托管/自动托管)中调用搜索REST服务

在其他上下文中访问搜索REST API(就像任何SP2013 REST API一样)可能会涉及更多。主要考虑因素是,如果您的页面是’t在SharePoint服务器上,您’需要处理OAuth并添加‘Authorization’REST请求的令牌。如果你’重新使用C#(即.NET CSOM),然后将TokenHelper类自动添加到Visual Studio项目即可简化此过程– you’将需要使用HttpWebRequest对象(或类似对象)来调用REST URL,然后处理响应。自从我’这次在服务器上访问并可以访问.NET,XML在这里可能是一个更好的选择:

为了清楚起见,在这里’我的ASPX页面– as you can see, I’m使用ASP.NET ListView控件将结果放在页面上:

旁注:其他情况–在云应用中使用jQuery调用SP2013 REST API

在研究这些样本时,我意识到另一种有用的方法是调用REST API 客户端 从云应用程序(以AJAX方式)。在这种情况下,我们’d可能是用jQuery编码的,因此需要处理获取OAuth令牌并将其添加到Web请求的过程–但是在这种情况下,我们不’没有TokenHelper支持我们。当然,您可能将TokenHelper包装在WCF服务中并从jQuery调用,但这意味着多个Web请求的处理效率低下(首先是对服务的请求,然后是交换以获取OAuth令牌,最后是对SharePoint的调用) REST API)。

最好是消除多余的调用,而直接获取OAuth令牌并直接从jQuery调用REST URL–但是需要一些工作来促进这一点。一世’确保有人会尽快查看,因为我希望它会成为SharePoint 2013应用程序开发的常见模式。

使用CSOM代替REST

我真的可以’不要再考虑太多原因了,但是如果CSOM是您的客户端首选,那么您’ll want Microsoft.SharePoint.Client.Search.Query.SearchExecutor.ExecuteQuery()。 

允许应用使用搜索的权限请求

为了能够从应用程序(任何托管类型)调用搜索REST API,需要授予该应用程序使用搜索服务的权限– this doesn’默认情况下不会发生。像任何与“core SharePoint”开发人员必须以权限请求的形式指定要求,而不是应用程序本身,并且安装应用程序的管理员必须同意在安装时授予此权限。

因此,开发人员需要将以下内容添加到appmanifest.xml文件中:

请注意,为 属性提供了一个非常描述性的字符串,提醒您什么’s being asked for –尽管应用中的大多数操作都会运行“as the app’s identity” (i.e. as the ‘app principal’),而是将以用户身份执行搜索(以及相关的安全性整理)。

在Visual Studio中 设计师 对于appmanifest.xml文件,如下所示:

SearchPermRequest

当然,当有人安装(或升级)该应用程序时,他们’将会将搜索列为应用程序所需的权限之一–并可以就此是否可以做出明智的决定:

Grant_SearchPermRequest

本地部署有问题吗?

因此,以上所有内容都非常适合Office 365环境。但是,当我将一个使用搜索的应用程序部署到本地开发人员环境时,我看到了一些奇怪的东西。我没有’尚未针对另一个本地环境测试该应用程序,但是我’d有兴趣听到是否有人看到此消息–该应用实际上无法添加,我看到一条消息说“Sorry, only 租户管理员s can add or give access to this app”:

OnPremisesError_AppWithSearchPermRequest

这让我感到好奇,原因有两个:

那么,还有其他人看到吗?我想念什么吗?请在评论中让我知道。

最后,它’值得停下来考虑–通过整个SharePoint环境中的搜索来获取内容的权限级别相对较高。基本上可以让该应用访问您企业中创建的内容,并且搜索索引中的所有内容都可用。显然,一个应用程序可能会做一些‘interesting’因此,在盲目安装之前对此类应用进行一些评估可能是明智的。

结论

通过应用调用搜索非常有价值,在这里我们’我们研究了如何从SharePoint托管的应用程序(在JavaScript中)和云应用程序(在C#中)进行操作。此处用于进行此类REST调用的模板可以应用于许多其他操作–最终,该模式是识别URL,在SharePoint外部进行身份验证,确保存在权限请求,然后深入JSON / XML响应以获取有价值的数据。

15条评论:

托比亚斯·齐默格伦(Tobias Zimmergren)说过...

克里斯很棒的文章!

扬·斯汀贝克说过...

很棒的文章,谢谢!

我必须说我'我对返回搜索结果的方式感到震惊。我希望使用JSON将单个结果解析为我的DTO,现在发现自己使用类似的东西;


$(data.d.query.PrimaryQueryResult.Table.Rows.results).each(function(){
var myData = new MyDTO({
标题:(this).Cells.results.cells [2] .Value
});
myArray.push(myData);
});

关于如何清除此混乱的任何想法?

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

@简

是的,我知道您为什么有这种需要。它'可以这么说,在您/我的代码中的某处'这将是深入了解并获取价值的动力。

我可以'还没说我有一个好的模式。也许某种JavaScript工厂通过获取JSON数据并返回DTO将其抽象化了一些?

克里斯。

库鲁什说过...

嗨,谢谢你的精彩文章。我有一个问题,我在一个普通的javascript文件中使用了您的代码JSOM,但是我在运行和调试它时将其添加到了MasterPage中。它在SP.RequestExecutor上抱怨并说未定义。谢谢你的帮助。

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

@Kourosh,

It'很难确定具体的代码,但是在代码执行之前,SharePoint所使用的核心JavaScript不能以某种方式执行。也许:

-正确的引用尚未添加到母版页
-引用(例如,对SP.js的引用)在* JavaScript代码下方*

准确检查所需JavaScript文件的一种好方法是创建一个SharePoint托管的应用程序,并查看所创建的默认ASPX页。

HTH,

克里斯。

匿名 said...

这段代码很好,但是使用onSuccess处理程序方法调用JSON.parse(data)不会发生。

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

@匿名,

对,就那个'可能有点多余的安全检查-它'可能没有必要。

干杯,

克里斯。

马可·范·维伦说过...

克里斯你好

我相信,如果默认情况下未启用任何租约,则所有创建的网站集都会添加到默认租户(应用程序域也是租户,最终整个事情都通过订阅和相应的设置进行管理)。

干杯,马可

匿名 said...

any news on the Sorry, only 租户管理员s can add or give access to this app”

我在365上遇到相同的问题(本地)

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

@匿名,

2013年3月发布的SP2013公共更新中已解决此问题。

HTH,

克里斯。

Butchersoft说过...

克里斯你好,

我获取了您的代码,并对其进行了更改,使其可以在现场SharePoint服务器上运行。我只需要更改runsearch身份验证的方式,一切就可以正常运行

继承人的变化
private void runSearch( string 询问Text)
{
如果(IsPostBack)
{
尝试
{

sharepointHostWeb =新的Uri(Request.QueryString ["SPHostUrl"]);
}
抓住
{
sharepointHostWeb =新的Uri(sharepointServer);
}
}

使用(clientContext = TokenHelper.GetS2SClientContextWithWindowsIdentity(sharepointHostWeb,Request.LogonUserIdentity))
{

accessToken = TokenHelper.GetS2SAccessTokenWithWindowsIdentity(sharepointHostWeb,Request.LogonUserIdentity);

字符串searchRestUrl = string.Format("/ _api / search / 询问?querytext ='{0}'", 询问Text);

HttpWebRequest请求=(HttpWebRequest)HttpWebRequest.Create(sharepointHostWeb.ToString()+ searchRestUrl);
request.Method ="GET";
request.Accept ="application/atom+xml";
request.ContentType ="application / 在 om + xml; type = entry";
request.Headers.Add("Authorization", "Bearer " + accessToken);

HttpWebResponse response =(HttpWebResponse)request.GetResponse();

//处理响应..
XDocument oDataXML = XDocument.Load(response.GetResponseStream(),LoadOptions.None);
XNamespace 在 om = "http://www.w3.org/2005/Atom";
XNamespace d = "http://schemas.microsoft.com/ado/2007/08/dataservices";
XNamespace m = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata";

清单项目= oDataXML.Descendants(d +"query")
。元素(d +"PrimaryQueryResult")
。元素(d +"RelevantResults")
。元素(d +"Table")
。元素(d +"Rows")
。元素(d +"element")
.ToList();

// N.B.可能有比这更优雅/有效的方法来从(有点尴尬)的XML中提取值。
var searchResults =来自项目中的项目
选择新的
{
标题= item.Element(d +"Cells").Descendants(d + "Key").First(a => a.Value == "Title").Parent.Element(d +"Value").Value,
作者= item.Element(d +"Cells").Descendants(d + "Key").First(a => a.Value == "Author").Parent.Element(d +"Value").Value,
点击突出显示摘要 = item.Element(d + "Cells").Descendants(d + "Key").First(a => a.Value == "点击突出显示摘要").Parent.Element(d +"Value").Value,
路径= item.Element(d +"Cells").Descendants(d + "Key").First(a => a.Value == "Path").Parent.Element(d +"Value").Value
};

//将数据绑定到ListView。
lvSearchResults.DataSource = searchResults;
lvSearchResults.DataBind();
}
}

其中-字符串sharepointServer ="http:// sharepoint13 / _vti_bin / Longitude5";

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

好东西,谢谢分享!

克里斯。

丹@德森夫说过...
此评论已被作者删除。
肖恩·多默说过...

伟大的文章克里斯。在尝试从其他Web应用程序调用_api / search / Suggest API时是否遇到任何问题(例如,从mysite宿主Web应用程序调用门户Web应用程序)?我收到所有SP_ResponseInfo属性未定义的错误。

有什么想法吗?

未知说过...

谢谢