SharePoint Client Object Model. Управляемый код

С этого поста я начну рассказывать о клиентской объектной модели (Client Object Model) SharePoint, о том как она работает и как можно её использовать. Сегодня я покажу как работает клиентская модель в управляемом коде на примере создания веб-части, отображающей последние новости с сайта www.eon-russia.ru. Этот сайт как раз построен на базе SharePoint Foundation.

Источник данных

Новости на сайте www.eon-russia.ru представляют из себя вики-страницы, расположенные в библиотеке "Новости" на узле /ru/content/. Сразу оговорюсь, что здесь никакого криминала нет, все эти данные доступны любому и проследить структуру сайта можно, используя для этого адрес в формате http://[server]/_laouts/viewlsts.aspx, и, "спускаясь" ниже по иерархии узлов.

Итак, мы будем отбирать последние 20 новостей и отображать эти данные на своем портале (где эти данные показывать совершенно не важно, хоть в WinForms-приложении, хоть в консольном приложении). Нас интересуют следующие поля:

  • ID - идентификатор новости;
  • Title1 - заголовок новости;
  • Summary - краткое описание новости;
  • PublicationDate - дата публикации новости;
  • Branch - филиал [Lookup];
  • PublishPlace - категория [Lookup].

При желании можно считывать ещё и поле WikiField. В нем хранится само содержимое вики-страницы. Класс, представляющий новость Е.ОН, у меня выглядит вот так:

Выбор данных

Для работы с клиентской моделью SharePoint 2010 нам понадобятся две сборки: Microsoft.SharePoint.Client и Microsoft.SharePoint.Client.Runtime. Вот код метода, возвращающего нам коллекцию объектов из списка SharePoint:

  1. public IEnumerable<EonPublication> GetPublications()
  2. {
  3.   var res = new List<EonPublication>();
  4.   using (var ctx = new ClientContext(_siteUrl))
  5.   {
  6.     ctx.AuthenticationMode = ClientAuthenticationMode.Anonymous;
  7.     var web = ctx.Web;
  8.     ctx.Load(web);
  9.  
  10.     var list = web.Lists.GetByTitle("Новости");
  11.     ctx.Load(web.Lists);
  12.     ctx.Load(list);
  13.  
  14.     var caml = new CamlQuery
  15.     {
  16.       ViewXml =
  17.         @"<View>
  18.           <Query>
  19.             <ViewFields>
  20.               <FieldRef Name='Title1' />
  21.               <FieldRef Name='Summary' />
  22.               <FieldRef Name='Branch' />
  23.               <FieldRef Name='PublicationDate' />
  24.               <FieldRef Name='PublishPlace' />
  25.             </ViewFields>
  26.             <OrderBy>
  27.               <FieldRef Name='PublicationDate' Ascending='FALSE' />
  28.             </OrderBy>
  29.           </Query>
  30.           <RowLimit>20</RowLimit>
  31.         </View>"
  32.     };
  33.     var listItems = list.GetItems(caml);
  34.     ctx.Load(listItems);
  35.     ctx.ExecuteQuery();
  36.  
  37.     foreach (var item in listItems)
  38.     {
  39.       var publication = new EonPublication(item.FieldValues);
  40.       res.Add(publication);
  41.     }
  42.  
  43.   }
  44.   return res.ToList();
  45. }

В начале мы создаем контекст вызывая конструктор класса ClientContext и передаем ему URL-адрес сайта. Если указанный адрес не будет начинаться с http:// или https://, то мы получим исключение ArgumentException. В нашем случае доступ к содержимому анонимный, что мы и указываем. Client object model в SharePoint'е так же поддерживает аутентификацию на основе форм (FBA) либо Default-аутентификацию (берется текущие учетные данные).

Очень важным здесь является понимание того, что происходит при вызове методов Load() и ExecuteQuery(). Их я опишу подробней.

Метод Load()

Этот метод предназначен для загрузки свойств объекта. Самой загрузки данных с сервера при этом не происходит. Вместо этого ClientContext строит коллекцию запросов. Метод Load() принимает в качестве параметров объект ClientObject и набор лямбда-выражений для выборочной загрузки свойств. Т.е. мы можем загрузить все свойства нашего объекта list вызвав метод Load():

ctx.Load(list);

Или можно явно указать свойства, которые мы хотим загрузить. Следующий код указывает, что необходимо загрузить Id, Title и SchemaXml объекта list:

ctx.Load(list,
    l => l.Title,
    l => l.Id,
    l => l.SchemaXml);

Если попытаться обратиться к свойству объекта ClientObject, значения которых не загружены с сервера, то мы получим исключение Microsoft.SharePoint.Client.PropertyOrFieldNotInitializedException .

Объектов, унаследованных от класса ClientObject здесь много. Стоит обратить внимание, что защищаемых объектов (унаследованных от SecurableObject) здесь только три (List, ListItem и Web). А вот объект Site, представляющий коллекцию сайтов защищаемым не является, т.к. это просто контейнер, в котором всегда есть минимум один сайт.

Классы, унаследованные от ClientObject

Метод ExecuteQuery()

Этот метод отвечает за загрузку данных с сервера. При это на сервер посылаются два запроса. Первый запрос обращается к службе /_vti_bin/sites.asmx и вызывает метод GetUpdatedFormDigestInformation для получения FormDigest. Второй запрос обращается к службе /_vti_bin/Client.svc для получения данных.

FormDigest

FormDigest, как следует из описания протокола, служит для обеспечения безопасности. Относится это к POST-запросам на сервер. Веб-приложение считывает FormDigest из параметра __REQUESTDIGEST или из заголовка X-RequestDigest. Полученное значение обязательно проходит валидацию (подробнее здесь).

В нашем случае в запрос добавляется заголовок X-RequestDigest:

  1. public override void ExecuteQuery()
  2. {
  3.   this.EnsureFormDigest();
  4.   base.PendingRequest.RequestExecutor.RequestHeaders["X-RequestDigest"] = this.m_formDigestInfo.DigestValue;
  5.   base.ExecuteQuery();
  6. }

После чего на стороне клиента формируется запрос, содержащий набор действий (Actions), которые необходимо выполнить на сервере и описание свойств объектов (ObjectPaths). В ответ на сервере формируется JSON и отправляется клиенту.

Демонстрационный проект

Демонстрационный проект

Скачать можно здесь.


Поделиться

Коментарии