enhancement
major
minor
major
minor
major
#29648
TL Views: Die über ClientResources ausgelieferten Skripte und Stylesheets tragen keine Version in der URL, der Browser verwendet nach einer Aktualisierung veraltete Client-Dateien
Kontext
Die React-Oberfläche lädt ihre Client-Dateien über den Dienst ClientResources (com.top_logic.layout.react.resource): Module melden module-script-, script- und stylesheet-Einträge an, und UnbundledResourceProvider schreibt die zugehörigen <script>-/`<link>`-Verweise sowie die Import-Map in den Seitenkopf. Das gilt für die Dateien der Engine selbst (tl-react-bridge.js, tl-react-controls.js, tlReactControls.css, die Bundles von chartjs/wysiwyg/codeedit, …) ebenso wie für die Module einer Anwendung.
Fehler
DefaultResourceResolver gibt die konfigurierte Ressource unverändert als URL aus. Nur webjar:-Verweise werden auf einen versionierten Pfad aufgelöst. Die URLs enthalten damit weder eine Version noch einen Zeitstempel, z.B. /style/tlReactControls.css oder /script/tl-react-bridge.js. Das Theme-Stylesheet _core_theme.css wird dagegen mit ?t=<Zeitstempel> ausgeliefert.
Folge: Nach dem Einspielen einer neuen Version verwendet der Browser aus seinem Cache weiterhin die alten Skripte und Stylesheets, während der Server schon die neuen Zustandsschlüssel und Kommandos verwendet. Das zeigt sich als falsche Darstellung und kann zu Fehlfunktionen führen, bis der Anwender den Cache leert oder die Seite hart neu lädt.
Reproduktion
- tl-demo-react starten, eine Ansicht öffnen.
- Ein über ClientResources angemeldetes Stylesheet ändern (z.B. tlReactControls.css), das Modul neu bauen und die Anwendung neu starten.
- Die Seite normal neu laden: Der Browser verwendet weiterhin die alte Datei.
Erwartetes Verhalten
Die Verweise auf ClientResources-Dateien tragen eine Kennung, die sich mit dem Inhalt ändert, oder mindestens mit jedem Start bzw. jeder Auslieferung. In Frage kommen ein Inhalts-Hash oder derselbe Zeitstempel-Mechanismus wie beim Theme-Stylesheet. Die Kennung muss auch in die Einträge der Import-Map einfließen, da Modul-Skripte untereinander über die Import-Map aufgelöst werden.
Lösung
- DefaultResourceResolver hängt an jede kontextrelative Ressource einen Versionsparameter ?v=<Hash> an. Der Hash besteht aus den ersten 12 Hex-Ziffern des SHA-256 über den Dateiinhalt, gelesen über den FileManager. Die URL ändert sich damit nur, wenn sich der Inhalt ändert – ein Neustart ohne Änderung verwirft den Browser-Cache nicht. webjar:-Verweise sind bereits versioniert und bleiben unverändert, ebenso absolute URLs; eine nicht auffindbare Datei wird mit einer Warnung protokolliert und ohne Parameter ausgegeben.
- Der Hash folgt auch Änderungen zur Laufzeit: Der Resolver merkt sich zu jedem Hash Änderungszeitpunkt und Größe der Datei in der entpackten Web-Anwendung und berechnet ihn beim nächsten Seitenaufbau neu, sobald sich diese ändern (z.B. wenn eine Datei in der Anwendung bearbeitet oder ein Bundle während der Entwicklung neu gebaut wird). Ressourcen, die nur außerhalb der entpackten Web-Anwendung (z.B. in einem Jar) liegen, werden einmal gehasht.
- UnbundledResourceProvider löst jede Ressource pro Seitenaufbau genau einmal auf, sodass Script-Tag und Import-Map-Eintrag eines Moduls stets dieselbe URL tragen und das Modul nur einmal instanziiert wird. Klassische Skripte erhalten kein zusätzliches ?t=-Suffix aus HTMLUtil mehr.
- CacheControlFilter liefert Anfragen, die den Versionsparameter tragen, mit einer eigenen, konfigurierbaren Lebensdauer aus (Init-Parameter versioned-max-age, Standard: ein Jahr, immutable), da sich deren Inhalt unter derselben URL nicht mehr ändert. Anfragen ohne Versionsparameter behalten die bisherige kurze Lebensdauer.