Architecture
Several parts share the work while the Document Editor runs in your application. Knowing which part does what pays off when you run the backend on another machine, put the application into a container or investigate a connection problem: you follow an editing session as it starts, survives a lost connection and ends, and you find where to configure the backend process.
The Document Editor page in the Overview introduces the editor and its frontends. If you haven't added the editor to an application yet, start with ASP.NET Core in Getting Started.
The Three Parts
The Document Editor is not a component that runs in the browser alone. Three parts work together, and all of them are required:
Browser: Document Editor frontend
|
| WebSocket connection, one per editor
v
Your ASP.NET Core application: TX Text Control WebSocket middleware
|
| TCP connection
v
Backend process: TXTextControl.Web.Server.Core
- The frontend runs in the browser. It shows the ribbon and the pages and sends every user action, such as typing, selecting or formatting, over its WebSocket connection. What it displays comes back from the server.
- The WebSocket middleware runs in your ASP.NET Core application.
app.UseTXWebSocketMiddleware()adds it, and for each editor that connects it creates a handler that passes the messages between the browser and the backend process. Because it handles the live traffic of all editors, Text Control recommends that applications with many concurrent users run heavy document processing in a separate application instance. - The backend process
TXTextControl.Web.Server.Corekeeps the documents that are open in the editors, synchronizes them and performs the layout and rendering work: cursor movement, layout recalculation and spell checking. It uses the Core engine and renders pages as SVG. The hosted serviceDocumentEditorWorkerManagerfrom theTXTextControl.Web.DocumentEditor.Backendpackage starts it together with your application.
All frontends use this chain: the ASP.NET Core HTML helper, the Angular and React components, the plain JavaScript page and the Blazor component. With Angular, React and JavaScript, only the frontend is a separate application; it still connects to an ASP.NET Core backend, or to a Node.js application in which the WebSocketServer of the @txtextcontrol/tx-websocket-server package takes the place of the middleware. Product Architecture shows how the editor fits into the rest of the product.
The Life of an Editing Session
Opening the Editor
When a page with the editor opens, the frontend connects to the WebSocket address of your application. The JavaScript frontend passes this address as webSocketURL to TXTextControl.init. The ASP.NET Core HTML helper builds it from TextControlSettings.APIRoute, whose default api/ leads to the path /api/TXWebSocket.
The server opens a session for the connection, and TXTextControl.connectionID returns its unique ID. When the editor is ready to be used from code, it raises the textControlLoaded event. Code that calls the JavaScript API therefore starts in a handler of this event; Events lists the events that follow during initialization.
Editing
Each editor has its own session and its own document on the backend.
Warning
Unsaved Documents. While a user edits, the document exists only in the memory of the backend process. It isn't stored anywhere until your code saves it, for example with
saveDocumentin the browser, and sends it to a database or file storage. Loading and Saving Documents shows how.
Two users who open the same file in two editors edit two independent copies. Live collaboration on one document is a separate package, TXTextControl.Web.Collaboration, which is available as a preview.
Losing and Restoring the Connection
The editor needs a constant connection to show the document. If the WebSocket connection drops, for example because of a short network interruption, the editor doesn't close the session right away:
- The frontend shows a message and tries to reconnect to the same session with its connection ID.
- If the connection comes back in time, the session continues. Changes that weren't saved yet are still there.
- If the reconnection attempts stop, the editor raises the
reconnectionFailedevent, and the session is lost.
DocumentEditorSettings.reconnectTimeout sets the time in seconds after which the editor stops trying. A user who reloads the page while the editor is reconnecting starts a new session, and unsaved changes are lost.
The following JavaScript page sets the timeout to 20 seconds and reports when the connection closes and when reconnecting fails:
TXTextControl.init({
containerID: "txDocumentEditor",
webSocketURL: "wss://localhost:7118/api/TXWebSocket",
editorSettings: {
reconnectTimeout: 20 // seconds
}
});
TXTextControl.addEventListener("textControlLoaded", function () {
TXTextControl.addEventListener("webSocketClosed", function (e) {
console.log("Connection closed: " + e.code + " " + e.reason + " (clean: " + e.wasClean + ")");
});
TXTextControl.addEventListener("reconnectionFailed", function (e) {
console.log("Reconnection failed: " + e.reason);
});
});
What the code does:
editorSettingspasses a DocumentEditorSettings object toinit, here withreconnectTimeout.- addEventListener attaches the handlers once the editor has been loaded.
- The
webSocketClosedhandler receives WebSocketClosedEventArgs with the close code, the reason and whether the connection was closed cleanly (wasClean). - The
reconnectionFailedhandler receives ReconnectionFailedEventArgs with the reason. Use it to tell the user that the document has to be opened again.
To protect unsaved work against a lost connection, save regularly. The blog article Best Practices: Reliable Auto-Save in TX Text Control Using the WebSocketHandler and Background Services saves the document on the server side.
Closing the Editor
The session ends when the connection closes. In single-page applications, call removeFromDom before a view with the editor is removed, as Remove the Editor in JavaScript API describes.
Configure the Backend Process
By default, DocumentEditorWorkerManager starts the backend process on the same machine, and app.UseTXWebSocketMiddleware() without parameters connects to it. You need a configuration when the default port is taken, when the backend has to listen on a specific network interface, when it runs on another machine, in a container or in several instances, or when it sits behind a load balancer.
Address and Port in Your Application
The options class DocumentEditorWorkerOptions sets the ListenAddress and Port of the backend process from a section of appsettings.json. Pass the same address and port to UseTXWebSocketMiddleware, for example app.UseTXWebSocketMiddleware("192.168.3.19", 4567); if the values don't match, the editor can't connect. Step 4 of "From the Old Document Editor Backend to the Backend Package" in Migrating from Previous Versions shows the complete configuration.
The Backend's Configuration File
The backend process also reads TXTextControl.Web.Server.Core.config.json, which the package deploys next to the worker files. It sets the TCP port, the IP versions and logging:
{
"Logging": {
"LogLevel": "Info",
"LogTarget": "File",
"FolderPath": "./logs",
"FileName": "TXTextControl.Web.Server.Core.{date}.log"
},
"TcpPort": 5664,
"SupportedIpVersions": "IPv4"
}
| Setting | Description |
|---|---|
TcpPort | The port on which the backend accepts the connections of the middleware |
SupportedIpVersions | IPv4, IPv6 or Both |
Logging.LogLevel | How much the backend logs: None, Error, Warn, Info or Debug |
Logging.LogTarget | Where the backend logs: Console or File. The file that the package deploys sets Console, and DocumentEditorWorkerManager forwards the console output to the logging of your application. |
Logging.FolderPath | The folder of the log files when LogTarget is File, by default ./logs |
Logging.FileName | The name of the log files when LogTarget is File; {date} is replaced with the current date in the format yyyyMMdd |
If you change the port here, pass it to the middleware as well: app.UseTXWebSocketMiddleware("localhost", 5664). When DocumentEditorWorkerOptions in appsettings.json also sets a value, that value takes precedence over the file.
Run the Backend Separately
The backend process doesn't have to run inside your web application. It can run on its own, for example on a dedicated machine, as a Windows service, as a Linux daemon or in a container, as long as the .NET runtime is installed. In that case:
- The TCP port must be reachable from the web application. Open it in the firewall, and in a container, expose and map it.
- Pass the address of the machine or container and the port to
UseTXWebSocketMiddleware. - Start the process with the address and port it listens on, as command-line arguments; The Connection to the Backend Process shows them.
The Deployment Guide describes the setups for Docker, Azure and Linux.
Scaling and Load Balancing
A session belongs to one backend process, because only that process holds the live document. Behind a load balancer, configure sticky sessions so that each WebSocket connection stays on the same node. Each open session needs memory on the backend; in Text Control's sizing guidance, 50 to 150 MB per session, depending on the loaded spell checking dictionaries. "Scaling Beyond One Instance" in Product Architecture describes how to split the workloads, and Performance and Scalability goes into detail.
The blog article Scaling TX Text Control Document Editor Applications explains the recommended setup for many concurrent users.
Next Steps
- Loading and Saving Documents: get documents into the session and back to your server.
- Events: the events of initialization, editing and the connection.
- WebSocket Architecture: the WebSocket connection in detail.
- WebSocket Security: protect the WebSocket endpoint of your application.