Back to "Einfaches 'Donut Hole' Caching mit HTMX"

This is a viewer only at the moment see the article on how this works.

To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk

This is a preview from the server running through my markdig pipeline

ASP.NET HTMX Razor

Einfaches 'Donut Hole' Caching mit HTMX

Thursday, 12 September 2024

Einleitung

Donut Loch Caching kann eine nützliche Technik sein, bei der Sie bestimmte Elemente einer Seite verbergen möchten, aber nicht alle. Allerdings kann es schwierig sein, umzusetzen. In diesem Beitrag werde ich Ihnen zeigen, wie man eine einfache Donut Loch Caching Technik mit HTMX implementieren.

Das Problem

Ein Problem, das ich mit dieser Website hatte, ist, dass ich Anti-Fälschungs-Tokens mit meinen Formularen verwenden wollte. Dies ist eine gute Sicherheitspraxis, um Cross-Site Request Forgery (CSRF) Angriffe zu verhindern. Es verursachte jedoch ein Problem mit dem Caching der Seiten. Das Anti-Forgery Token ist für jede Seitenanforderung einzigartig, wenn Sie also die Seite zwischenspeichern, wird das Token für alle Benutzer gleich sein. Dies bedeutet, dass, wenn ein Benutzer ein Formular abgibt, das Token ungültig ist und die Formular-Einreichung fehlschlägt. ASP.NET Core verhindert dies durch Deaktivierung aller Caching auf Anfrage, wenn das Anti-Fugery Token verwendet wird. Dies ist eine gute Sicherheitspraxis, aber es bedeutet, dass die Seite überhaupt nicht zwischengespeichert wird. Dies ist nicht ideal für eine Website wie diese, wo der Inhalt meist statisch ist.

Die Lösung

Ein üblicher Weg um dies herum ist 'Donut-Loch'-Caching, wo Sie die Mehrheit der Seite, aber bestimmte Elemente zwischenspeichern. Es gibt eine Reihe von Möglichkeiten, dies in ASP.NET Core mit dem Teilansicht-Framework zu erreichen, aber es ist komplex zu implementieren und erfordert oft spezifische Pakete und Konfiguration. Ich wollte eine einfachere Lösung.

Da ich bereits die ausgezeichnete HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX HTMX In diesem Projekt gibt es eine super einfache Möglichkeit, dynamische 'Donut-Loch'-Funktionalität durch dynamisches Laden von Partials mit HTMX zu erhalten. Ich habe schon darüber geloggt Verwendung von AntiForgeryRequest Tokens mit Javascript aber wieder das Problem war, dass diese effektiv deaktiviert Caching für die Seite.

JETZT kann ich diese Funktionalität wieder einsetzen, wenn ich HTMX benutze, um Partials dynamisch zu laden.

<li class="group relative mb-1">
    <div  hx-trigger="load" hx-get="/typeahead">
    </div>
</li>

Dead einfach, nicht wahr? Alles, was dies tut, ist in die eine Zeile des Codes im Controller zu rufen, der die Teilansicht zurückgibt. Das bedeutet, dass das Anti-Forgery Token auf dem Server generiert wird und die Seite wie gewohnt zwischengespeichert werden kann. Die Teilansicht wird dynamisch geladen, so dass das Token immer noch für jede Anfrage einzigartig ist.

HINWEIS: Wenn Sie einen 'SPA'-ähnlichen Ansatz verwenden, wie ich es mit HTMX tue, müssen Sie sicherstellen, dass die load Event feuert nicht wieder auf der Rückseite Taste. Ich mache dies, indem ich den Typeahead einsetze, um das Ziel bei der ersten Last zu überschreiben.

  <div id="typeaheadelement" hx-trigger="load" hx-get="/typeahead"  hx-target="this" hx-swap="outerHTML"></div>

Dies bedeutet, dass das erste Mal läuft es löscht den Ursprung div und ersetzt ihn durch den neuen Inhalt aus dem Teil zurückgegeben von der Steuerung unten. Da das Anto-forgery Token auf dem Server erzeugt und in einem Session-Cookie gespeichert wird, sollte es mit diesem Ansatz noch funktionieren (bis ich die App neu deploye).

Wir stellen die hx-target zu this hauptsächlich, um einen JS-Fehler zu vermeiden; da HTMX ein gültiges Ziel benötigt, wenn die Anfrage abgeschlossen wird.

    [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
    [HttpGet("typeahead")]
    public IActionResult TypeAhead()
    {
        return PartialView("_TypeAhead");
    }

Innerhalb des Teils haben wir noch die schlichte einfache Form mit dem Anti-Fälschungs-Token.

<div x-data="window.mostlylucid.typeahead()" class="relative" id="searchelement"  x-on:click.outside="results = []">
    @Html.AntiForgeryToken()
    <label class="input input-sm dark:bg-custom-dark-bg bg-white input-bordered flex items-center gap-2">
        <input
            type="text"
            x-model="query"
            x-on:input.debounce.300ms="search"
            x-on:keydown.down.prevent="moveDown"
            x-on:keydown.up.prevent="moveUp"
            x-on:keydown.enter.prevent="selectHighlighted"
            placeholder="Search..."
            class="border-0 grow  input-sm text-black dark:text-white bg-transparent w-full"/>
        <i class="bx bx-search"></i>
    </label>
    <!-- Dropdown -->
    <ul x-show="results.length > 0"
        class="absolute z-10 my-2 w-full bg-white dark:bg-custom-dark-bg border border-1 text-black dark:text-white border-b-neutral-600 dark:border-gray-300   rounded-lg shadow-lg">
        <template x-for="(result, index) in results" :key="result.slug">
            <li
                x-on:click="selectResult(result)"
                :class="{
                    'dark:bg-blue-dark bg-blue-light': index === highlightedIndex,
                    'dark:hover:bg-blue-dark hover:bg-blue-light': true
                }"
                class="cursor-pointer text-sm p-2 m-2"
                x-text="result.title"
            ></li>
        </template>
    </ul>
</div>

Dies verkapselt dann den gesamten Code für die Typeahead-Suche und wenn er eingereicht wird, zieht er das Token und fügt es der Anfrage hinzu (genau wie zuvor).

        let token = document.querySelector('#searchelement input[name="__RequestVerificationToken"]').value;
            console.log(token);
            fetch(`/api/search/${encodeURIComponent(this.query)}`, { // Fixed the backtick and closing bracket
                method: 'GET', // or 'POST' depending on your needs
                headers: {
                    'Content-Type': 'application/json',
                    'X-CSRF-TOKEN': token // Attach the AntiForgery token in the headers
                }
            })

Schlussfolgerung

Dies ist ein super einfacher Weg, um 'Donut Loch' Caching mit HTMX zu bekommen. Es ist ein guter Weg, um die Vorteile des Caching ohne die Komplexität eines zusätzlichen Pakets zu erhalten. Ich hoffe, Sie finden das nützlich. Lassen Sie mich wissen, wenn Sie irgendwelche Fragen in den Kommentaren unten haben.

logo

©2024 Scott Galloway