Работа с API SharePoint из локального SPFx Workbench

При работе с Framework (SPFx) разработчик может использовать как локальный сценарий, так и удаленную отладку.

Во время запуска gulp serve стартует локальный сервер со средой исполнения веб-частей. Локальный воркбенч доступен по ссылке https://localhost:4321/temp/workbench.html, он представляет собой страницу, которая немного похожа на Modern UI SharePoint'а, однако это просто локально хостящаяся страница на ней нет возможности работы с данными из SharePoint.

В локальном режиме предполагается создание искусственных ответов от API в виде mock-данных, подготовленных заранее и лежащих прямо в отладочной части кода. В локальном ворбенче работает горячая перезагрузка страницы по мере изменений в проекте, это очень удобная возможность.

Зачастую mock-данных недостаточно, так же их подготовка, актуализация и сверка по формату с настоящими ответами от API допольно трудоемка.

SPFx так же можно отлаживать непосредственно на странице SharePoint в рамках удаленного воркбенча, рамполагающегося по пути [SHAREPOINT_WEB_URL]/_layouts/15/workbench.aspx. Можно запустить gulp serve --nobrowser (--nobrowser блокирует автоматическое открытие страницы локального ворбенча) и размещать разрабатываемые веб-части в ворбенче. Либо даже использовать специальные отладочные параметры вставляемые на любой странице. Такой режим уже полноценно позволяет работать с API.

На этом моменте можно спросить "Ладно, буду использовать удаленный ворбенч или отладочную адресную строку. Зачем вообще тогда локальный ворбенч?". На самом деле очень логичный и хороший вопрос. Но, поверьте, всегда есть какие-то случаи и сценарии применения, где бы не помешала бы опция "локально, но с API". Совершенно случайно, такая опция есть, давайте рассмотрим.

Настройка SPFx проекта для использования прокси

Для начала нам нужен SPFx проект.

1. В SPFx проекте устанавливаем API прокси.

npm install sp-rest-proxy --save-dev

sp-rest-proxy зависимость уровня разработки, позволяет аутентифицироваться и маршрутизировать запросы в API SharePoint без проблем с аутентификацией и CORS.

2. Создаем proxy.js где-нибудь в проекте со следующим содержимым:

const CertificateStore = require('@microsoft/gulp-core-build-serve/lib/CertificateStore');
const RestProxy = require('sp-rest-proxy');

const settings = {
  port: 4323,
  protocol: 'https',
  ssl: {
    cert: CertificateStore.default.instance.certificateData,
    key: CertificateStore.default.instance.keyData
  }
};

const restProxy = new RestProxy(settings);
restProxy.serve();

Скачать скрипт ]

Для того, чтобы можно было обращаться к прокси со страницы воркбенча, прокси должен использовать SSL и быть запущен на HTTPS, соответственно. Переиспользуем SSL сертификаты из состава SPFx (тем самым gulp trust-dev-cert и gulp untrust-dev-cert распространяются не только на SPFx ворбенч, но и для прокси).

3. Сздаем npm дазачу для запуска прокси, например: "proxy": "node ./proxy".

4. Для подключения в SharePoint запускаем node ./proxy или только что созданную задачу npm run proxy.

Во время первого запуска в консоли должен появиться мастер подключения к SharePoint, где указывает Site URL и прочие желаемые авторизационные параметры. Подробности можно узнать в репозитории проекта.

5. Настроить задачу запуска прокси и локального веб-сервера одновременно, например с помощью concurrently или npm-run-all.

Настройка concurrently:

5.1 Установить зависимость:

npm install concurrently --save-dev

5.2 Добавить задачу запуска в package.json:

"scripts": {
  ...
  "serve": "concurrently --kill-others "npm run proxy" "gulp serve""
  ...
}

Теперь при запуска задачи будет стартовать и прокси и локальный сервер SPFx:

npm run serve

На этом настрока проекта завершена и можно рассмотреть аспекты, связанные с разработкой.

Определение режима работы веб-части

Благодаря использованию прокси, SPFx решение может обращаться в API даже будучи запущенным локально. Все, что нам требуется реализовать, так это определение режима и переключение между адресами.

Предположим, что сайт SharePoint расположен по адресу https://contoso.sharepoint.com/sites/site/my_web.

SPFx веб-части, по большому счету, все равно, где она будет размещена. Фактический адрес сайта предоставлябется через SPFx контекст: в объекте this.context.pageContext.web есть absoluteUrlserverRelativeUrl, пр. Запроса к API должны динамически определять нужный эндпойнт.

В локальном режиме, напротив, SPFx ничего не знает о том, где расположен SharePoint. И мы ответствтенны за определение и указание правильного адреса. Однако, с прокси, этот адрес задан в настройках и чаще всего он будет всегда одинаковый https://localhost:4323, относительный путь до веба при этом должен быть определен где-то в настройках.

В локальном режиме веб-часть должна сформировать путь до API эндпойнтов в виде https://localhost:4323/sites/site/my_web/_api/.... При этом, всегда нужно оперировать с абсолютными путями.

Когда это понятно, становится очевидно, что нужно определять режим "локальный" или "удаленный". В SPFx есть соответствующий хелпер (нет даже необходимости в чем-то на подобии window.location.href.indexOf('https://localhost:') === 0):

import { Environment, EnvironmentType } from '@microsoft/sp-core-library';

if (Environment.type === EnvironmentType.Local) {
  // Local mode
} else {
  // Online mode
}

Используя режимы, мы можем задавать нужные корректные эндпойнты.

Бонус - настройка PnPjs

Как многие знают, я причастен к проекту PnPjs и предпочитаю использовать данную библиотеку на проектах.

PnPjs прекрасно работает совместно с SPFx, помимо этого рад сообщить, что и в связке с API Proxy все хорошо. Требуемая настройка проста:

import { Web, setup } from '@pnp/sp';
import { proxyUrl, webRelativeUrl } from './../settings';
// settings.ts should be created with corresponding exports

...
let web: Web;
if (Environment.type === EnvironmentType.Local) {
  web = new Web(`${proxyUrl}${webRelativeUrl}`);
} else {
  // On SharePoint page sp-pnp-js should be configured with
  setup({ spfxContext: this.context });
  // or a Web object should be created with explicit web URL
  web = new Web(this.context.pageContext.web.absoluteUrl);
}
// Then universal pnp code and dealing with SPFx specifics
/*
web.lists.get()
  .then(...).catch(...);
*/
...

Пример

По следующей ссылке представлен пример проекта, в котором настроено все вышеописанное. 


Опубликовано: 17.12.2018
Автор: Андрей Кольтяков