SharePoint in Office 365 – Site Collection übergreifend Daten anziehen

In den meisten SharePoint Projekten – sei es on-premise oder in der Cloud – kommt man früher oder später an den Punkt, an dem Daten über Site Collections hinweg angezogen werden sollen. Diese Grenzen sind in on-premise Lösungen mit dem Objektmodel (SPSite, SPWeb, SPList, usw.) relativ einfach zu umgehen. In der Cloud und mittels clientseitigen SharePoint Technologien (z.B. mit CSOM) hingegen kann dies auf den ersten Blick nicht ganz so einfach realisiert werden. Doch mit der ausgebauten REST-Api in SharePoint 2013 ist dies trotzdem auf einfache und effiziente Art und Weise möglich. In einem kleinen Beispiel möchte ich aufzeigen, wie mittels einfachem REST Call Websites Site Collection übergreifend angezogen werden können.

Das wichtigste vorneweg ist wie bereits erwähnt, die REST-Api von SharePoint 2013. Diese versteckt sich jeweils unter dem Pfad /_api/. Die Api besitzt diverse Endpunkte. Die Abfragen können zudem mittels OData Abfragevorgängen verfeinert werden.
Dazu hier zwei kleine Beispiele für REST Calls auf die Api.
Einfacher REST Call um das Root Web einer Site Collection anzuziehen:

http://sp2013url//sites/dev/_api/Web

REST Call mit OData Abfrage, um Titel und Url einer Linkliste anzuziehen:

http://sp2013url//sites/dev/_api/web/lists('GUID')?$select=Title,Url

Zurück zu meinem Beispiel. Ich erstelle eine ganz normale SharePoint Autohosted App, welche aus einer SharePoint App und einer ASP.NET Anwendung besteht. Die Site Collection übergreifenden Daten möchte ich in einem WebPart anzeigen, also lege ich in der SharePoint App via „Add Item“ ein Client Web Part an. Ebenso erstelle ich im darauf folgenden Dialog eine neue Web Part Page. Diese wird im Folder „Pages“ in meinem ASP.NET Projekt angelegt. Meine erstellte Solution sieht nun folgendermassen aus.

1_clientwebpart

In diesem WebPart möchte ich nun alle Websites einer andern Site Collection anzeigen. Dies werde ich, wie bereits beschrieben, mittels REST Calls umsetzen. Der Grund, warum ich dies nicht komplett auf dem Client (mit JavaScript) löse ist der, dass ich mit serverseitigen Techniken (C#) angezogenen Dateien zum Beispiel einfach cachen kann und für die gesamten REST Verarbeitung Tools wie RestSharp und für die deserialisierung der Daten Newtonsoft.Json verwenden kann. Beide DLLs sind auf NuGet verfügbar.

Der Aufruf auf SharePoint erfolgt über die SharePoint API mit folgendem Aufruf:

http://sp2013url/andere/sitecollection/_api/Web/Webs

Wichtig ist, dass der SharePoint App die benötigten Permissions auf den gesamten Tenant gegeben werden. Gibt man dies nicht an, erfolgt bei Abfragen eine Access Denied Meldung. Diese Rechte können im Manifest.xml der SharePoint App gegeben werden.

<AppPermissionRequests AllowAppOnlyPolicy="true">
   <AppPermissionRequest Scope="http://sharepoint/content/tenant" Right="FullControl" />
   <AppPermissionRequest Scope="http://sharepoint/content/sitecollection" Right="FullControl" />
</AppPermissionRequests>

Mittels der angesprochenen RestSharp Assembly lässt sich der Aufruf sehr vereinfacht ausführen. Dazu hab ich folgende Methode geschrieben.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static WebItemCollection SharePointRestCall(string pSiteUrl, HttpRequest pRequest)
{
// get the token string from the current HttpRequest and create a valid SharePointContextToken
var contextTokenString = TokenHelper.GetContextTokenFromRequest(pRequest);
var contextToken = TokenHelper.ReadAndValidateContextToken(contextTokenString, pRequest.Url.Authority);
// get the OAuth access token response for the foreign site collection
var sharePointUrl = new Uri(pSiteUrl);
var accessToken = TokenHelper.GetAccessToken(contextToken, sharePointUrl.Authority);
// define the rest api url
var restClient = new RestClient(pSiteUrl + "/_api/");
// add json as response and define the deserialization implementation
restClient.AddHandler("application/json", new SharePointJsonDeserializer());
// add the access token
restClient.Authenticator = new OAuth2AuthorizationRequestHeaderAuthenticator(accessToken.AccessToken, "Bearer");
// creat and execute the rest request
var request = new RestRequest("web/Webs", Method.GET);
request.AddHeader("Accept", "application/json;odata=verbose");
// execute and return the WebItemCollection
var data = restClient.Execute(request).Data;
return data;
}

Da die SharePoint REST Api den Response immer in einem „d“-Objekt zurück gibt, soll das deserialisierte Objekt auch nur diesen Teil zurück geben. Dazu kann vom Interface IDeserializer eine Implementierung erstellt werden, wie sie im obigen Beispiel bereits angefügt ist.

1
2
3
4
5
6
7
8
9
10
public class SharePointJsonDeserializer : IDeserializer
{
   public T Deserialize(IRestResponse response)
   {
      return JsonConvert.DeserializeObject(JObject.Parse(response.Content)["d"].ToString());
   }
   public string RootElement { get; set; }
   public string Namespace { get; set; }
   public string DateFormat { get; set; }
}

Dank Newtonsoft.Json kann der Response der RESP Api direkt auf POCOs gemappt werden.
Der Aufbau der WebItemCollection sieht wie folgt aus:

1
2
3
4
5
6
7
8
9
public class WebItemCollection
{
   public List Results { get; set; }
}
public class WebItem
{
   public string Url { get; set; }
   public string Title { get; set; }
}

Schlussendlich können im erstellten WebPart File in der Page_Load Methode mit folgendem Code alle Websites der fremden Site Collection ausgegeben werden.

1
2
3
4
5
6
7
8
var urlItem = new UrlItem { SiteUrl = "http://foreign/sitecollection/url", Title = "Blank"};
data = RestApi.SharePointRestCall(urlItem.SiteUrl, Request);
Response.Write("<ul><li><a href='" + urlItem.SiteUrl + "'>" + urlItem.Title + "</a></li><ul>");
foreach (var item in data.Results)
{
   Response.Write("<li><a href='" + item.Url + "'>" + item.Title + "</a></li>");
}
Response.Write("</ul></ul>");

Zum Abschluss sieht das WebPart folgendermassen aus.

2_clientwebpart

Blank ist dabei der Name des Root Webs, News und Tasklist der darin enthaltenen Sub Webs.

2 Gedanken zu “SharePoint in Office 365 – Site Collection übergreifend Daten anziehen

  1. Danke für das interessante Beispiel. Versteh ich richtig, dass es keine sogenannte „SP-hosted App“ ist, sondern eine „provider-hosted app“. Du verwendest ja server-seitigen Code, der auf einem separaten Webserver gehostet werden muss. Der Grund dafür liege in der Möglichkeit, server-seitig zu cachen und weitere Bibliotheken (DLL) aufzurufen.
    Richtig?
    –Sam

    PS: ich bin kein (.Net) Entwickler und versuche das neue SP13 Modell zu verstehen.

    • Hi Sam

      Genau, dies ist eine Provider-Hosted App – in diesem Beispiel direkt als Auto-Hosted App in Windows Axure umgesetzt.
      Auch dein zweiter Punkt ist richtig. Ich verwende server-seitigen Code für ein mögliches Caching (im Beispiel konnte ich so den normalen ASP.Net Cache verwenden) und für mich bequemeres Programmieren mit den angesprochenen Bibliotheken. Natürlich gibt es für die Entwicklung mit JavaScript ähnliche Bibliotheken. Caching wäre aber in diesem Sinne (serverseitiges Caching) mit einer SharePoint-Hosted App nicht möglich.

      Beste Grüsse
      Sämi

Hinterlasse eine Antwort

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *

*

*

Du kannst folgende HTML-Tags benutzen: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>