Кэширование данных при использовании Microsoft Graph JavaScript SDK

Кэшируя данные в приложениях JavaScript для совместной работы, подключенных к Microsoft 365, вы можете значительно повысить их производительность и взаимодействие с пользователем. Вот как это сделать.

Приложения для совместной работы в Microsoft 365

Миллионы пользователей ежедневно сотрудничают в Microsoft 365. Подключая свои приложения к данным и аналитическим данным, которые они создают в Microsoft 365, вы можете создавать приложения для совместной работы, которые помогут им работать эффективнее и эффективнее. Microsoft Graph — это API для данных вашей организации, хранящихся в Microsoft 365. А чтобы помочь вам использовать Microsoft Graph в своих приложениях, Microsoft предлагает SDK для самых популярных языков программирования.

Подключайте приложения JavaScript к Microsoft 365

Чтобы подключить приложение JavaScript к Microsoft 365, используйте Microsoft Graph JavaScript SDK. Он предлагает вам простой способ выполнения запросов к Microsoft Graph. Более того, он обрабатывает ответы, включая исключения, за вас, что позволяет вам сосредоточиться на создании своего приложения!

Чтобы начать использовать Microsoft Graph JavaScript SDK, создайте экземпляр клиента Graph с поставщиком проверки подлинности. После этого вы готовы вызывать Microsoft Graph для получения и создания данных в Microsoft 365.

Получение тех же данных из Microsoft 365: эффективно?

Microsoft Graph JavaScript SDK следует вашим инструкциям и извлекает запрошенные вами данные из Microsoft 365. Но по умолчанию он не кэширует полученные данные.

Допустим, вы создаете приложение, в котором хотите показать список людей в вашей команде и файлы, над которыми вы все недавно работали. Если для каждого файла вы хотите показать человека, который изменил файл, вы в конечном итоге загрузите информацию об одном и том же человеке несколько раз. Добавьте к нему аватарку человека, и вы увидите множество повторяющихся запросов. Итак, что вы можете с этим поделать?

Простым решением было бы добавить какое-то кэширование. Но означает ли это, что вам нужно будет изменить все запросы Microsoft Graph, которые вы отправляете в своем приложении? Не совсем! Microsoft Graph JavaScript SDK предлагает лучший способ сделать это. Проверьте это.

ПО промежуточного слоя кэширования для Microsoft Graph JavaScript SDK

Microsoft Graph JavaScript SDK создан с использованием промежуточного программного обеспечения. ПО промежуточного слоя состоит из классов с различными обязанностями, которые применяются к каждому запросу и ответу, выполняемому пакетом SDK Graph. По умолчанию Graph SDK имеет промежуточное ПО для обработки аутентификации, регулирования и выполнения веб-запросов. Но что действительно здорово, так это то, что вы можете создать собственное промежуточное ПО и подключить его к клиенту Microsoft Graph. Это позволяет запускать пользовательский код при каждом запросе, не изменяя сами запросы — идеально подходит для реализации кэширования!

Вот пример ПО промежуточного слоя для кэширования, которое можно использовать с Microsoft Graph JavaScript SDK для кэширования данных в хранилище сеансов браузера:

export function BrowserCacheMiddleware(expirationConfig) {
  this.nextMiddleware = undefined;
  this.expirationConfig = expirationConfig;

  const getHeaders = (headers) => {
    const h = {};
    for (var header of headers.entries()) {
      h[header[0]] = header[1];
    }
    return h;
  };

  const blobToDataUrl = async (blob) => {
    return new Promise((resolve, reject) => {
      var reader = new FileReader();
      reader.onload = function () {
        var dataUrl = reader.result;
        resolve(dataUrl);
      };
      reader.readAsDataURL(blob);
    });
  };

  // from https://stackoverflow.com/a/16245768
  const dataUrlToBlob = (dataUrl) => {
    const blobData = dataUrl.split(',');
    const contentType = blobData[0].replace('data:', '').replace(';base64', '');
    return b64toBlob(blobData[1], contentType);
  };

  const b64toBlob = (b64Data, contentType = '', sliceSize = 512) => {
    const byteCharacters = atob(b64Data);
    const byteArrays = [];

    for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
      const slice = byteCharacters.slice(offset, offset + sliceSize);

      const byteNumbers = new Array(slice.length);
      for (let i = 0; i < slice.length; i++) {
        byteNumbers[i] = slice.charCodeAt(i);
      }

      const byteArray = new Uint8Array(byteNumbers);
      byteArrays.push(byteArray);
    }

    const blob = new Blob(byteArrays, { type: contentType });
    return blob;
  }

  const getExpiresOn = (url) => {
    if (!this.expirationConfig) {
      return undefined;
    }

    for (var i = 0; i < this.expirationConfig.length; i++) {
      const exp = this.expirationConfig[i];
      if (url.indexOf(exp.path) > -1) {
        const expiresOn = new Date();
        expiresOn.setMinutes(expiresOn.getMinutes() + exp.expirationInMinutes);
        return expiresOn;
      }
    }

    return undefined;
  }

  return {
    execute: async (context) => {
      console.debug(`Request: ${context.request}`);

      const requestKey = btoa(context.request);
      let response = window.sessionStorage.getItem(requestKey);
      let now = new Date();
      if (response) {
        const resp = JSON.parse(response);
        const expiresOn = resp.expiresOn ? new Date(resp.expiresOn) : undefined;
        if (!expiresOn || expiresOn > now) {
          console.debug('-- from cache');

          let body;
          if (resp.headers['content-type'].indexOf('application/json') > -1) {
            body = JSON.stringify(resp.body);
          }
          else {
            body = dataUrlToBlob(resp.body);
          }
          context.response = new Response(body, resp);
          return;
        }
        else {
          console.log('-- cache expired');
        }
      }

      console.debug('-- from Graph');
      await this.nextMiddleware.execute(context);

      if (context.options.method !== 'GET' ||
        context.response.status !== 200) {
        // don't cache non-GET or failed requests
        return;
      }

      const resp = context.response.clone();

      const expiresOn = getExpiresOn(resp.url);
      // reset date to catch expiration set to 0
      now = new Date();
      // don't cache if the item already expired
      if (expiresOn <= now) {
        return;
      }

      const headers = getHeaders(resp.headers);
      let body = '';
      if (headers['content-type'].indexOf('application/json') >= 0) {
        body = await resp.json();
      }
      else {
        body = await blobToDataUrl(await resp.blob());
      }

      response = {
        url: resp.url,
        status: resp.status,
        statusText: resp.statusText,
        headers,
        body,
        expiresOn
      };
      window.sessionStorage.setItem(requestKey, JSON.stringify(response));
    },
    setNext: (next) => {
      this.nextMiddleware = next;
    }
  }
};

Точка входа находится в функции execute, которая получает объект context с запросом и ответом. Используя URL-адрес запроса, функция создает ключ кеша, который затем используется для проверки того, был ли запрос ранее отправлен и все еще находится в кеше. Если это так, он возвращает ранее кэшированные данные. Если нет, он передает запрос следующему промежуточному программному обеспечению в цепочке. Когда запрос завершен, он проверяет, успешен ли ответ, и кэширует полученные данные. Данные хранятся в кэше сеанса, и функция достаточно умна, чтобы поддерживать как двоичные, так и текстовые данные.

Что еще хорошо в этом промежуточном программном обеспечении, так это то, что вы можете настроить продолжительность кэширования для каждого пути запроса. Например, вы можете кэшировать файлы на 5 минут, но при этом исключить задачи, чтобы всегда получать последний список. Вы можете передать конфигурацию, которая имеет отношение к вашему приложению, при создании экземпляра ПО промежуточного слоя кэша.

Чтобы использовать промежуточное ПО кэширования, его необходимо включить при создании экземпляра клиента Graph из SDK:

Вы должны добавить ПО промежуточного слоя кэша в начало массива ПО промежуточного слоя, используя middleware.unshift(). Запросы и ответы проходят через классы промежуточного ПО в том порядке, в котором они добавляются в массив промежуточного ПО. Если вы добавите промежуточное ПО кеша в конец массива промежуточного ПО, все запросы будут отправляться к Graph в обход кеша, что противоречит его назначению.

При наличии этого промежуточного ПО Graph SDK будет выполнять запросы только в первый раз. Если данные были получены ранее, а кеш все еще действителен, данные будут обслуживаться из кеша, что значительно повысит производительность вашего приложения.

Прежде чем ты уйдешь

Microsoft Graph JavaScript SDK можно использовать как в браузере, так и на сервере в Node.js. Промежуточное ПО кэширования, которое я показал вам здесь, предназначено для использования в браузере. Если вы хотите использовать его на сервере, вам нужно изменить две вещи.

Во-первых, вам нужно заменить window.sessionStorage на альтернативу, доступную на сервере в Node.js. Имейте в виду, что он должен правильно обрабатывать несколько интерфейсов (поэтому не храните кеш в памяти сервера). Затем трижды проверьте, не происходит ли утечка данных между сеансами (например, передача данных одного пользователя другому). Это было бы настоящей катастрофой.

к вам

Добавление кеша в ваше приложение JavaScript с помощью Microsoft Graph JavaScript SDK — отличный способ легко повысить производительность вашего приложения. Кэширование данных, получаемых вашим приложением из Microsoft 365, поможет вам уменьшить количество запросов к Microsoft Graph и значительно улучшить взаимодействие с пользователем.

Я показал вам, как вы можете создать промежуточное ПО для кэширования самостоятельно, но если вы хотели бы, чтобы SDK поддерживал его изначально, пожалуйста, проголосуйте за сопутствующую проблему в репозитории SDK. А если у вас есть вопросы или идеи по улучшению, оставляйте комментарии!

📣 Подпишитесь на мою рассылку

Понравилась эта статья? Подпишитесь на мою рассылку новостей и получайте больше советов и рекомендаций из области, доставленных прямо на ваш почтовый ящик.

Первоначально опубликовано на https://blog.mastykarz.nl 28 мая 2021 г.