Wymiana komunikatów między formatkami Dynamics 365 oraz zewnętrznymi komponentami

Dynamics 365, JavaScipt

Mechanizm rozszerzania interfejsu użytkownika w systemie Dynamics 365 za pomocą niestandardowych zasobów sieciowych lub zewnętrznych stron umieszczanych wewnątrz elementu IFRAME jest z całą pewnością znany każdemu, kto choć raz próbował zaimplementować jakieś niestandardowe zachowanie interfejsu omawianej aplikacji. W najprostszych przypadkach zagnieżdżone w ten sposób komponenty służą jedynie do statycznej prezentacji danych pochodzących z innych źródeł lub systemów. Bardziej zaawansowane scenariusze dają użytkownikowi możliwość edycji tychże danych, a także wykonywania rozmaitych, niepowiązanych ze standardowymi funkcjonalnościami Dynamics 365 czynności. Osobnym przypadkiem są komponenty (zaimplementowane zazwyczaj w postaci zasobów sieciowych), które operują jedynie na usługach sieciowych systemu Dynamics, udostępniając informacje w sposób, który ułatwia prace z danymi w systemie. Przykładami takich komponentów mogą być np. formatki prezentujące zbiorcze informacje pochodzące z wielu encji, komponenty umożliwiające ich edycję w ramach jednego ekranu lub prezentujące te dane za pomocą kontrolek, które nie są dostępne w „pudełkowym” systemie.

Zastanówmy się teraz nad sytuacją, kiedy wewnątrz dostosowywanej formatki Dynamics 365 powinien zostać wyświetlony komponent znajdujący się na innym serwerze. Teoretycznie nie powinno stanowić to żadnego problemu.  Niestety, jeżeli będziemy musimy zaimplementować jakąś interakcję między formatką CRM oraz prezentowaną za pomocą IFRAME stroną zewnętrzną, to na pewno napotkamy na problemy, które opiszę poniżej. Przykładami kłopotliwych funkcjonalności mogą być:

  • Automatyczne wykonanie jakiejś akcji na stronie umieszczonej wewnątrz formatki Dynamics po naciśnięciu przycisku w tym systemie
  • Odwołanie się do kontekstu strony CRM lub użycie API Xrm.Page.* przez komponent umieszczony wewnątrz elementu IFRAME.

W przypadku zasobu sieciowego lub strony znajdującej się na tym samym serwerze co Dynamics 365 wystarczyłoby użycie składni w rodzaju:

  • window.parent.Xrm.* (z poziomu strony wewnątrz IFRAME)
  • Xrm.Page.ui.controls.get(‚MyIFrame’).getObject().contentWindow.* (z poziomu formatki Dynamics 365).

W przypadku interakcji ze komponentem sieciowym, który jest umieszczony na zewnętrznym serwerze, stosowanie powyższych metod zakończy się niepowodzeniem i komunikatem o braku dostępu („Access Denied”) lub niemożliwości odczytu obiektu do którego odwołujemy się w kodzie formatki. Powodem takiego zachowania jest fakt, że współczesne przeglądarki ze względu na bezpieczeństwo domyślnie blokują komunikacje między stronami, które znajdują się na różnych serwerach lub/i w różnych domenach lub sieciach (więcej informacji na powyższy temat znajdziecie na stronie: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS). Podobne do wyżej opisanych problemy rozwiązuje się najczęściej za pomocą mechanizmu Cross-origin resource sharing (CORS), konfigurowalnego na poziomie serwera WWW, aplikacji lub bezpośrednio w kodzie źródłowym tworzonego rozwiązania. Czasem jednak możemy znaleźć się w sytuacji, z której z różnych powodów nie będziemy mogli dokonać ww. modyfikacji. Może być to spowodowane polityką organizacji, sytuacją w której serwery są hostowane przez zewnętrznych dostawców lub też brakiem możliwości modyfikacji kodu źródłowego w pożądanym obszarze.

Na szczęście z pomocą przychodzi nam w tym przypadku JavaScriptowe API Window.postMessage(). Omawiana metoda umożliwia bezpieczna komunikację między 2 systemami wewnątrz przeglądarki internetowej. Dokładny opis działania ww. mechanizmu znajdziecie pod adresem: https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage. W tym miejscu wspomnę jedynie o tym, że ww. API umożliwia komunikację po stronie klienta między serwisami hostowanymi na różnych serwerach.  Jedna ze stron pełni w tym modelu komunikacji rolę nadawcy, a druga odbiorcy. Komunikacja może być również dwukierunkowa. Obie aplikacje mogą jednocześnie wysyłać oraz odbierać wysyłane wiadomości.

 

Po stronie nadawcy – implementujemy następującego „Listenera”, który umożliwia nasłuchiwanie oraz obsługę odbieranych wiadomości:

Event.origin jest w powyższym przypadku adresem strony będącej nadawcą wysyłanego komunikatu. W omawianym przypadku zaimplementowany Listener nie obsłuży żadnego komunikatu pochodzącego ze strony innej niż my.source.com.

W kodzie strony pełniącej rolę nadawcy, wysyłka komunikatu przebiega w następujący sposób:

Pierwszy z argumentów funkcji postMessage jest wysyłanym komunikatem (w powyższym przypadku jest to obiekt obj zserializowany do formatu JSON), natomiast drugim z argumentów jest adres strony odpowiedzialnej za odbiór wiadomości.

Jak to wszystko ma się do systemu Dynamics 365? Wyobraźmy sobie sytuację, w której na formatce Dynamicst 365 wyświetlamy stronę znajdującą się zewnętrznym serwerze, która w określonym momencie musi zmodyfikować zawartość swojego „rodzica”. Próba odwołania się do kontekstu Dynamics 365 zakończy się w tym przypadku niepowodzeniem, podobnie jak wykorzystywanie „łamańców” w rodzaju window.parent.Xrm.Page.*. Z pomocą przychodzi nam w tym przypadku właśnie wspomniane powyżej API oraz funkcja postMessage. Formatka Dynamics 365 będzie pełnić w tym przypadku rolę odbiorcy. Nadawcą komunikatu będzie natomiast strona zewnętrzna, którą będziemy chcieli „zagnieździć” wewnątrz interfejsu Dynamics 365.

Ponownie, teoretycznie wszystko powinno działać. Niestety, dodanie listenera bezpośrednio do formatki systemu Dynamics 365 nie jest możliwe. Wynika to z faktu, że omawiany system używa wewnętrznie ramek oraz IFrame’ów do natywnego wyświetlania danych wewnątrz przeglądarki. Z pomocą programistycznych bogów, selektorów JQuery oraz wyrażeń warunkowych moglibyśmy jakoś (metoda „kleju i sznurka”) podłączyć omawiany listener do formatki Dynamicsa. Metoda ta nie jest jednak oczywiście wspierana przez Microsoft, istnieje więc duże prawdopodobieństwo, że zaimplementowana w ten sposób funkcjonalność „wybuchnie” przy pierwszej aktualizacji systemu.

W jaki sposób ominąć opisywany problem? Wystarczy umieścić na formatce systemu Dynamics zasób sieciowy będący stroną HTML. Zasób ten powinien posiadać element IFRAME wewnątrz którego prezentowana będzie strona umieszczona na zewnętrznym serwerze. Wewnątrz kodu omawianego komponentu implementujemy listener, odpowiedzialny za odbieranie przychodzących komunikatów. Ponieważ tworzony zasób sieciowy jest hostowany bezpośrednio na serwerze aplikacji Dynamics – w kodzie obsługującym przychodzący komunikat możemy odwoływać się do kontekstu omawianego systemu (za pomocą biblioteki ClientGlobalContext.js.aspx) lub bezpośrednio do API Xrm.Page.*.

Ufff… To już wszystko w omawianym temacie. Powyższa metoda nie jest może szczególnie piękna i może budzić opory architektów-purystów (IFRAME wewnątrz IFRAME? WTF?). Jest ona jednak skutecznym i działającym sposobem, zapewniającym możliwość wewnątrz-przeglądarkowej komunikacji między systemem Dynamics 365 i zewnętrznymi komponentami w pewnych (opisanych przeze mnie powyżej) specyficznych scenariuszach i konfiguracjach.

Dzisiejszy artykuł sponsorują następujące „hasztagi”:

#ProgramistyczneŁamańce #DrogaSamurajaDynamics365

Total Views: 221 ,

Dodaj komentarz