Lire le HTML de l’éditeur de texte enrichi avec Blazor Server

Schéma : le bouton Save demande une IJSStreamReference à l’interopérabilité JS, le navigateur encode getHTMLCode() en octets et .NET les lit avec OpenReadStreamAsync ; une simple chaîne de plus de 32 Ko ferme le circuit

Tôt ou tard, une page Blazor Server doit récupérer le HTML de l’éditeur en C# pour l’enregistrer. L’appel évident, JS.InvokeAsync<string>, fonctionne pendant les tests puis échoue au premier long document, car le résultat transite par la connexion SignalR du circuit, dont les messages sont limités à 32 Ko. Cette page montre ce qui s’est passé quand nous avons atteint cette limite, et comment l’exemple lit plutôt le HTML sous forme de flux.

La limite de 32 Ko

Avec Blazor Server, chaque message du navigateur vers le serveur passe par le circuit, y compris le résultat d’un appel d’interopérabilité JS. Le MaximumReceiveMessageSize de SignalR pour le circuit vaut 32 Ko par défaut. Nous avons renvoyé editor.getHTMLCode() via InvokeAsync<string> et mesuré :

  • 20,000 caractères de HTML : reçus.
  • 40,000 caractères : le navigateur a journalisé « Connection disconnected with error 'Error: Server returned an error on close: Connection closed with an error.' ». Le message de la page n’a pas changé, et le circuit avait disparu.

Au niveau de journalisation par défaut, l’échec ne laisse aucune trace côté serveur : il est donc facile de passer à côté pendant les tests.

Renvoyer des octets depuis JavaScript

Quand une fonction JS renvoie un Uint8Array, Blazor peut le transmettre à .NET sous forme d'IJSStreamReference, envoyé par morceaux en dehors de la limite des messages. Les fonctions d’interopérabilité de l’exemple se trouvent dans App.razor :

Razor · Components/App.razor
<script>
    // Called from Home.razor. The editor options stay in JavaScript, where they can also hold
    // functions (custom buttons, event handlers) that cannot be sent from .NET as JSON.
    window.rteBlazor = {
        create: function (host) {
            host.rte = new RichTextEditor(host, {
                toolbar: "full",
                galleryEndpoint: "/rte/gallery",
                maxUploadFileSize: 4000000 // same limit as the server, checked before the upload starts
            });
        },
        // Returned as bytes: .NET reads them as a stream, so documents over Blazor's 32 KB
        // SignalR message limit still arrive.
        getHtml: function (host) {
            return new TextEncoder().encode(host.rte.getHTMLCode());
        },
        destroy: function (host) {
            if (!host || !host.rte) return;
            // editor.destroy() exists only in newer editor builds
            if (typeof host.rte.destroy === "function") host.rte.destroy();
            host.rte = null;
            host.innerHTML = "";
        }
    };
</script>

getHtml encode le HTML en UTF-8 avec TextEncoder. L’éditeur lui-même est créé par create ; Ajouter une galerie d’images à un éditeur de texte enrichi avec Blazor explique create et destroy.

Lire le flux en C#

Razor · Components/Pages/Home.razor
async Task Save()
{
    var bytes = await JS.InvokeAsync<IJSStreamReference>("rteBlazor.getHtml", host);
    await using var stream = await bytes.OpenReadStreamAsync(maxAllowedSize: 10_000_000);
    using var reader = new StreamReader(stream);
    var html = await reader.ReadToEndAsync();
    message = $"Saved {html.Length} characters of HTML."; // save html to your database here
}
  • InvokeAsync<IJSStreamReference> demande les octets sous forme de flux plutôt que de valeur.
  • OpenReadStreamAsync(maxAllowedSize: 10_000_000) fixe le maximum que le serveur lira. Sa valeur par défaut est 512,000 octets : réglez-la sur ce que peut contenir votre colonne de base de données.
  • StreamReader retransforme les octets UTF-8 en chaîne. Enregistrez-la à partir de là, et nettoyez-la avant de l’afficher à d’autres utilisateurs.

Avec ce code, un document de 100,000 caractères x plus une image insérée est arrivé complet : « Saved 100116 characters of HTML. »

Pourquoi ne pas relever la limite

L’autre solution consiste à augmenter MaximumReceiveMessageSize dans les options du hub, par exemple via AddInteractiveServerComponents().AddHubOptions(...). Cela relève la limite pour chaque message de chaque circuit. Nous ne l’avons ni utilisé ni testé pour cette page. Le streaming conserve la valeur par défaut et ne laisse passer que le HTML, jusqu’à la taille que vous choisissez.

Les images sont un cas différent : elles ne passent jamais par le composant. Téléverser des images depuis un éditeur de texte enrichi avec Blazor explique pourquoi l’éditeur les envoie à des points de terminaison HTTP, si bien que le HTML enregistré ne contient que leurs URL.

Tester

Lancez le projet d’exemple de ce guide avec dotnet run et ouvrez http://localhost:5304. Collez un long document dans l’éditeur, ou tapez quelques paragraphes et collez-les de nombreuses fois, puis cliquez sur Save. Le message sous l’éditeur indique le nombre de caractères arrivés en C#, bien au-delà de 32 Ko. Pour voir la limite, modifiez Save pour qu’elle lise le HTML avec InvokeAsync<string> depuis une fonction JS qui renvoie getHTMLCode() sous forme de chaîne, puis enregistrez de nouveau le même document : le circuit se ferme.

À lire ensuite