enhancement
major
minor
major
minor
Kontext
Eine Anwendung der React-Oberfläche (.view.xml-Ebene) kann einer anonymen Sitzung anstelle der Anwendung eine Login-Seite zeigen, indem die Wurzel von app.view.xml ein <switch> über currentUser().accountIsAnonymous() ist: im anonymen Fall wird eine View mit dem Login-Formular (<login/>-Aktion) gerendert, sonst die <app-shell> mit der <sidebar>.
Wird in dieser Situation ein Deep-Link wie /view/my-refunds ohne Sitzung aufgerufen, geht die Route verloren: Die Login-Seite enthält keinen RoutingParticipant, der das Segment my-refunds beansprucht, deshalb verwirft RouteManager.finishAdoption() das nicht aufgelöste Segment und korrigiert die Adresszeile auf /view/ (so dokumentiert: "Such a segment is dropped and the address bar is corrected to what the display composes"). Nach der erfolgreichen Anmeldung lädt LoginAction.completeLogin() die Seite per window.location.reload() neu – inzwischen unter /view/, sodass der Anwender auf der Startseite statt auf der angeforderten Seite landet.
Beobachtet in der Expenses-Anwendung (Login-Seite in app.view.xml, 2026-09-13): goto('/view/my-refunds') ohne Sitzung zeigt die Login-Seite unter /view/; nach dem Login ist "Meine Spesen" (die erste Seite) aktiv.
Erwartung
Ein Deep-Link, der eine Anmeldung erfordert, wird nach der Anmeldung erreicht: Der Anwender landet auf der Seite, die er aufgerufen hat.
Analyse
Die angeforderte URL geht an vier Stellen verloren, zwei davon bevor der RouteManager sie überhaupt sieht:
- Erster Aufruf ohne Cookie: die Cookie-Prüfung in TopLogicServlet.initSessionCheck() leitet auf die konfigurierte Startseite (/view/) um.
- Keine Sitzung: ViewServlet.handleNoSession() meldet das anonyme Konto an und leitet per redirectToStartPage() wieder auf /view/ um; nur der Query-String überlebt.
- Anonyme Sitzung vorhanden: die Login-Anzeige beansprucht kein Segment, RouteManager.finishAdoption() korrigiert die Adresszeile auf /view/.
- Nach der Anmeldung: PendingSessionAction.apply() leitet auf die aktuelle URL um, ViewServlet verwirft die Route aber auf der ersten Anfrage einer getauschten Sitzung (SESSION_SWAPPED) zugunsten der persönlichen Startseite.
Zusätzlich geben die SSO-Schaltflächen (<login-methods/>, LoginMethodsElement) fest /view/ als Rücksprungziel an.
Lösung
Konfigurierte Login-View. Die Anwendung erklärt in der ViewConfig neben default-view und entry-points optional eine login-view:
<config config:interface="com.top_logic.layout.view.ViewConfig" login-view="login-page.view.xml" />
Ist sie gesetzt und die Sitzung anonym, rendert das ViewServlet diese View, unabhängig davon, was die URL benennt (Route, Standard-View, Einstiegspunkt oder eine nicht registrierte View). Das Routing ist dabei ausgesetzt: RouteManager.holdUrl() hält die angeforderte URL, statt sie zu übernehmen – nichts wird aus der Anzeige zusammengesetzt, nichts korrigiert, auch nicht bei Vor/Zurück im Browser. Die Adresszeile zeigt also weiterhin /view/my-refunds, während das Login-Formular angezeigt wird, und der Reload nach der Anmeldung landet dort. Das Framework liefert dazu login-page.view.xml (das Anmeldeformular auf einer zentrierten Karte) mit; das Formular selbst ist in login-form.view.xml ausgelagert und wird vom Login-Dialog login.view.xml und von der Seite gemeinsam verwendet. Eine Anwendung braucht den <switch> über accountIsAnonymous() in app.view.xml nicht mehr.
Sitzungsaufbau behält die URL. Nach der Cookie-Prüfung und nach der anonymen Anmeldung leitet das ViewServlet auf die angeforderte URL (Pfad und Query) zurück statt auf die konfigurierte Startseite (AbstractTopLogicServlet.getEntryPage(), vom ViewServlet überschrieben; die klassischen Servlets behalten ihr Verhalten).
Eine Route in der URL gilt auch nach der Anmeldung. Die Sonderregel, die auf der ersten Anfrage einer getauschten Sitzung die Route verwirft, entfällt: die persönliche Startseite gilt nur für eine URL ohne Route, wie bei jeder anderen Anfrage auch. Dass ein abgemeldeter Benutzer seine Seite nicht an den nächsten vererbt, stellt die Abmeldung selbst sicher: <logout-command> navigiert ausdrücklich zur Wurzel der Anwendung (/view/).
SSO kehrt zur angeforderten Seite zurück. <login-methods/> übergibt die gehaltene Route als Rücksprungziel statt /view/.
Tastatur auf einer Seite ohne Dialog. Die Enter-Taste löste auf der Login-Seite die Anmeldung nicht aus, im Dialog schon: Tastenbindungen von Schaltflächen (useKeyboardBinding) landeten nur in einem Scope, den Dialog, Fenster oder Tabelle bereitstellen; außerhalb davon wurden sie stillschweigend verworfen. Der Keyboard-Dispatcher stellt jetzt einen äußersten Seiten-Scope bereit, in den solche Bindungen fallen; ein modaler Dialog liegt weiterhin darüber und fängt alle Gesten ab.
Weiteres. <panel> erhält eine allgemeine Option width (eigene Breite statt der Containerbreite, damit ein Container die Karte zentrieren kann). Die React-Demo (tl-demo-react) konfiguriert die Login-View und verlangt damit für jede Anfrage eine Anmeldung; docs/faq/react-view-layer.md beschreibt Anmeldung, anonyme Sitzungen und die Login-View.
Nicht Teil dieses Tickets: eine Anwendung mit öffentlichem Teil und geschütztem Bereich (z. B. /view/admin als anonymer Besucher). Dort wird ein Segment, das kein Teilnehmer beansprucht, weiterhin verworfen; nach der Anmeldung bleibt der Anwender auf der Seite, auf der er war.