Разработка
17
Окт
2

Как CRM кэширует Веб-ресурсы

С появлением CRM 2011 в системе появились функционал Веб-ресурсов, что сильно облегчило формирование и перенос решений между средами. Вместе с функционалом Веб-ресурсов появился и функционал их кэширования, который обеспечивает более быструю генерацию веб-страниц CRM. При этом важно понимать, как CRM это делает, чтобы избежать потенциальных проблем (например, загрузки Веб-ресурса с каждым запросом).

MS CRM поддерживает несколько методов для доступа к Веб-ресурсам:

  • $webresource – директива используется для ссылки на Веб-ресурсы в SiteMap’е или Ленте;
  • Привязка Веб-ресурса к форме записи;
  • Относительный URL;
  • Абсолютный URL.

И только три первых обеспечивают кэширование Веб-ресурса.

Рассмотрим следующий URL Веб-ресурса, который мог быть сгенерирован CRM при использовании $webresource или когда Веб-ресурс подключен к форме CRM:

https://crmServer/orgName/%7B634594655730000000%7D/WebResources/test.html

Обратите внимание на странное число в строке URL. Это специальный маркер, который CRM автоматически подставляет в URL Веб-ресурса и который гарантирует, что на клиенте в каждый момент времени используется последняя версия требуемого Веб-ресурса. Данный маркер генерируется в момент изменения (публикации) Веб-ресурса.

Работает это так:

  1. Браузер запрашивает в CRM страницу. При этом каждый Веб-ресурс, на который в странице имеется ссылка (правильно оформленная – см. ниже), возвращается с маркером последней версии;
  2. Браузер ищет в своем кэше (в папке вроде C:\Users\userName\AppData\Local\Microsoft\Windows\Temporary Internet Files) этот Веб-ресурс по полному совпадению URL. И если совпадение найдено, то Веб-ресурс берется из кэша;
  3. Если же совпадение не найдено запрос Веб-ресурса отправляется на сервер, а полученный ответ кэшироваться. Но не все запросы Веб-ресурсов кэшируются…

Срок кэширования Веб-ресурсов

Если воспользоваться инструментом вроде Fiddler’а, чтобы исследовать ответ CRM сервера (на запрос Веб-ресурса), то можно заметить, что у него в заголовке параметры кэширования, которые устанавливают срок кэширования в течение одного года.

У каждого Веб-ресурса, возвращенного из CRM, есть время хранения в кэше браузера. По истечении этого времени Веб-ресурс считается не «ликвидным» 🙂 и повторно запрашивается с сервера. Если Веб-ресурс запрашивается по URL с маркером последней версии, то в заголовке HTTP-ответа (можно посмотреть с помощью Fiddler’а) будет написано, что срок кэширования такого Веб-ресурса равен одному году.


Если маркер последней версии будет удален из URL Веб-ресурса (т.е. URL станет абсолютным), то в заголовке ответа будет (в зависимости от версии CRM) либо «не кэшировать», либо в поле срок кэширования будет указана текущая дата. Именно по этой причине запросы Веб-ресурсов по абсолютному URL не могут кэшироваться и каждый раз запрашиваются с сервера.


Как гарантировать кэширование Веб-ресурсов?

Рецепт не сложный: в большинстве случаев CRM генерирует корректный URL с маркером последней версии. Главное – не изобретать велосипед…

Встраивание Веб-ресурсов в форму объекта

Если Вы встраиваете Веб-ресурс в форму объекта (подключаете JS Веб-ресурс к форме или выводите HTML Веб-ресурс на страницу), то ссылка на них уже будет содержать маркер последней версии – тут Вам беспокоиться не нужно.

З.Ы. Это не относится к ссылкам на Веб-ресурсы внутри HTML-страницы, встроенной в форму – здесь Вам нужно самостоятельно следить за их корректностью (см. ниже).

Ссылки на Веб-ресурсы

При вставке ссылок на Веб-ресурсы в HTML-страницу всегда используйте относительный путь.

  • Пример #1
    Если у вас имеется два Веб-ресурса:

    • new_account.html
    • new_common.js

    то на странице new_account.html относительная ссылка будет выглядеть следующим образом:

    <script src="../Webresources/new_common.js" type="text/javascript"></script>
    
  • Пример #2
    Если у вас имеется два таких Веб-ресурса:

    • new_/Account/Account.html
    • new_/scripts/Common.js

    то на странице Account.html относительная ссылка будет такая:

    <script src="../scripts/Common.js" type="text/javascript"></script>
    

И ни в коем случае не формируйте абсолютный URL:

<script src="/Webresources/new_common.js" type="text/javascript"></script>

Или так:

<script src="http://crmServer/orgNmae/Webresources/new_common.js" type="text/javascript"></script>

SiteMap и Лента

Просто всегда используйте директиву $webresource в SiteMap’е и Ленте и CRM всегда будет генерировать кэшируемый URL.

Открытие диалогового окна с Веб-ресурсом

Тут у Вас два варианта:

  • Использовать служебную функцию openWebResource – это гарантирует кэширование Веб-ресурса:
    Xrm.Utility.openWebResource("new_page.htm");
    
  • Если по каким-либо причинам Вы не можете использовать openWebResource, то Вам нужно либо использовать абсолютный URL (в результате чего Вы остаетесь без кэширования), либо попытаться сгенерировать маркер последней версии вручную.
    А тут у нас уже появляется большой простор для фантазии 🙂 В порядке усложнения:

    • Использовать при формировании URL до Веб-ресурса глобальную JS-переменную WEB_RESOURCE_ORG_VERSION_NUMBER, которая уже корректный текущий маркер последней версии;
    • Можно попробовать найти текущий маркер последней версии, у какого-либо другого Веб-ресурса, который в настоящее время загружен. Примерно так:
      function GetCacheDirectory(webresourceUrl) {
          var scripts = document.getElementsByTagName("script");
          for (var i = 0; i < scripts.length; i++) {
              var url = scripts[i].src;
              var p1 = url.search("/%7B")
              if (p1 > 0) {
                  var p2 = url.search("%7D/", p1) + 4;
                  var resourceCache = url.substr(p1, (p2 - p1));
                  break;
              }
          }
          var url = "/" + Xrm.Page.context.getOrgUniqueName() + resourceCache + webresourceUrl;
          return url;
      }
      
      // Формирование полного URL Веб-ресурса с маркером последней версии
      var url = GetCacheDirectory('/WebResources_/new/Account/AssignAccount.htm');
      
      // Открытие Веб-ресурса
      window.open(url);
      
    • И самый хитрожопый 🙂 вариант – генерировать маркер последней версии самостоятельно! Главное тут понять, что маркер последней версии – это временная метка, которая представляет собой время изменения (в виде тиков UTC) последнего измененного Веб-ресурса.
      Соответственно, чтобы сгенерировать маркер последней версии необходимо получить последний измененный Веб-ресурс и дату его изменения и соответствующим образом обработать эту дату:

      function getWebResourceUrl(webResourceName) {
      
          var dotNetMillisecondsAt_1970_01_01 = 62135596800000,
              ticksPerMillisecond = 10000,
              lastWebResourceModifiedOn = null,
              webResourceCachingToken = null,
              webResourceUrl = "/WebResources/" + webResourceName;
      
          // Получаем дату изменения последнего измененного Веб-ресурса
          $.ajax({
              async: false,
              beforeSend: function (xhr, settings) {
                  xhr.setRequestHeader("Accept", "application/json");
              },
              contentType: "application/json; charset=utf-8",
              datatype: "json",
              global: false,
              success: function (data, textStatus, xhr) {
      
                  // Извлекаем дату изменения
                  lastWebResourceModifiedOn = +(/\/Date\((\d*)\)\//.exec(data.d.results[0].ModifiedOn)[1]);
      
                  // Конвертируем дату в маркер последней версии
                  webResourceCachingToken = (lastWebResourceModifiedOn + dotNetMillisecondsAt_1970_01_01) * ticksPerMillisecond;
              },
              type: "GET",
              url: Xrm.Page.context.prependOrgName("/XRMServices/2011/OrganizationData.svc/WebResourceSet()?$select=ModifiedOn&$orderby=ModifiedOn%20desc&$top=1")
          });
      
          // Формируем кэшируемый URL
          if (webResourceCachingToken !== null) {
              webResourceUrl = "/%7B" + webResourceCachingToken + "%7D" + webResourceUrl;
          }
      
          // Возвращаем URL
          return Xrm.Page.context.getServerUrl().replace(/\/$/, "") + Xrm.Page.context.prependOrgName(webResourceUrl);
      }
      
Комментарии (2)
  • mihadov 17.10.2015

    Вопросик: можно ли добится, чтобы IE вёл себя по этой схеме?
    тоесть мне приходится каждый раз после загрузки скрипта и публикации, ручками чистить кеш вместо простого F5

  • slivka_83 17.10.2015

    Странное поведение, ослик так должен вести себя по умолчнию.

*

code