HTMX con ASP.NET Core Partials: Il Server-Side Rinascimento (Italiano (Italian))

HTMX con ASP.NET Core Partials: Il Server-Side Rinascimento

Friday, 28 November 2025

//

18 minute read

Introduzione

Se hai costruito le tradizionali applicazioni ASP.NET Core MVC , si conosce il problema: che temuto " fare clic su flash " quando gli utenti navigano tra le pagine . Ricarica pagina completa , il browser cromata flickering , contenuti saltando in giro come la nuova pagina rende . Funziona , ma non ensazione di Moderno.

La tecnica di restituire frammenti HTML parziali dal server e di scambiarli nel DOM risolve questo - e non è nuova. Ho usato questo modello fin dai giorni jQuery, e anche prima con vaniglia JavaScript e XMLHttpRequest. Quello che è cambiato è come elegante E' diventato con l'HTMX.

HTMX ci offre un modo dichiarativo per fare quello che abbiamo sempre fatto: restituire HTML fornito dal server e scambiarlo nella pagina. Niente più scrittura JavaScript personalizzato per ogni interazione. Niente più scelta tra lo sviluppo "proprio" lato server e l'esperienza utente fluida. Con HTMX, otteniamo entrambi.

Articolo Companion: Questo articolo si concentra sul lato ASP.NET Core integrazione. Per una profonda immersione in eventi HTMX, ciclo di vita, e estensioni personalizzate, vedere il mio articolo compagno: A Whistle-stop Tour di estensioni HTMX e l'utilizzo di HTMX con ASP.NET Core.

In questo articolo, vi mostrerò come HTMX si integra splendidamente con ASP.NET Core parziale, come il eccellente HTMX.NET biblioteca rende ancora meglio, e come il mio per lo piùlucid.pagingtaghelper Il pacchetto NuGet utilizza HTMX fornisce una potente paginazione con una configurazione minima.

L'approccio è framework-agnostico troppo - Django ha aggiunto frammenti di template nella versione 6.0, Rails ha Turbo Frames, e l'ecosistema web più ampio sta abbracciando HTML-over-the-wire pattern. E 'un buon momento per essere la costruzione di applicazioni dotate di server.

Che cos'è HTMX?

HTMX è una libreria che consente di accedere alle moderne funzionalità del browser direttamente da HTML, piuttosto che scrivere JavaScript. Si estende HTML con attributi che consentono di effettuare richieste AJAX, scambiare contenuti e creare interazioni ricche - il tutto senza lasciare il tuo markup.

Gli attributi chiave che userai più spesso:

  • hx-get, hx-post, hx-put, hx-delete - Effettuare richieste HTTP
  • hx-target - Specificare dove mettere la risposta
  • hx-swap - Controllare come il contenuto viene scambiato (HTML interno, HTML esterno, ecc.)
  • hx-trigger - Definire cosa innesca la richiesta (click, cambio, carico, ecc.)
  • hx-push-url - Aggiornare l'URL del browser senza ricaricare l'intera pagina

Ecco la bellezza: stai ancora scrivendo codice server-side, restituendo server-rendered HTML. Nessuna API JSON, nessun modello client-side, nessun build pipeline. Solo un buon vecchio stile HTML sopra il filo.

Un modello vecchio come AJAX se stesso

Prima di HTMX, abbiamo ottenuto lo stesso effetto con molta più cerimonia. Ecco come sono stati gli aggiornamenti parziali nell'era jQuery:

// jQuery circa 2010
$('#load-more').click(function() {
    $.ajax({
        url: '/posts/page/' + currentPage,
        success: function(html) {
            $('#posts-container').append(html);
            currentPage++;
        }
    });
});

E anche prima, con vaniglia JavaScript:

// Vanilla JS circa 2005
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
    if (xhr.readyState === 4 && xhr.status === 200) {
        document.getElementById('content').innerHTML = xhr.responseText;
    }
};
xhr.open('GET', '/partial-content', true);
xhr.send();

Il modello lato server era identico - restituire frammenti HTML, scambiarli nel DOM. HTMX sposta solo questa logica da JavaScript in attributi HTML, rendendolo dichiarativo, scovabile, e molto meno error-prone. L'innovazione non è la tecnica; è l'interfaccia.

Gli sviluppatori di ASP.NET lo fanno da anni. AggiornarePanel in WebForms (2005), PartialView() in MVC dal primo giorno, Html.RenderAction() per i frammenti composabili - la capacità è sempre stata presente. Ciò che ci mancava era un modo elegante e standardizzato per collegarlo sul lato client. HTMX riempie perfettamente questo spazio.

Anche l'industria più ampia sta riscoprendo questi modelli. Termini come "SSR" (Server-Side Rendering), "hybrid rendering" e "ilands architecture" stanno essenzialmente descrivendo ciò che i framework sul lato server hanno sempre fatto, ma con occhi nuovi. È la validazione che le scale di approccio rese dal server, le prestazioni e - con gli strumenti giusti - forniscono un'eccellente esperienza utente.

Impostazione di HTMX in ASP.NET Core

In primo luogo, includere HTMX nel vostro layout. È possibile utilizzare una CDN o servirla localmente:

<script src="https://unpkg.com/[email protected]"></script>

Niente build step, niente installazione di npm, niente configurazione di webpack.

Alpine.js: il componente Client-Side

Per la reattività lato client (mostrare/nascondere elementi, stati di commutazione, stato UI locale), Alpine.js completa perfettamente HTMX. A soli 15KB gzipped, fornisce Vue/React-like reattività dichiarativa senza il bloat.

<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js"></script>

Ecco come lavorano insieme:

<div x-data="{ open: false }">
    <button x-on:click="open = !open">Toggle</button>
    <div x-show="open" x-transition>
        <button hx-get="/api/data" hx-target="#results">Load Data</button>
    </div>
</div>

Alpine gestisce l'interfaccia utente locale (il toggle), HTMX gestisce le chiamate del server (il recupero dati). In questo articolo, vedrete questo modello - Alpine per la reattività del client, HTMX per le interazioni del server.

La libreria HTMX.NET

Khalid Abuhakmeh's HTMX.NET libreria fornisce l'integrazione core ASP.NET di prima classe. Htmx e Htmx.TagHelpers NuGet pacchetti, si sente nativo di .NET e rende lavorare con HTMX un piacere assoluto. Troverete più di Khalid eccellente lavoro open-source sul suo GitHubCity name (optional, probably does not need a translation).

Installazione

dotnet add package Htmx
dotnet add package Htmx.TagHelpers

Nella tua _ViewImports.cshtml:

@addTagHelper *, Htmx.TagHelpers

Il metodo di estensione IsHtmx()

La caratteristica più utile è la Request.IsHtmx() metodo di estensione, che ti dice se la richiesta è arrivata da HTMX. Questo consente di restituire una vista completa o solo una parziale:

[HttpGet]
public async Task<IActionResult> Index(int page = 1, int pageSize = 20)
{
    var posts = await blogViewService.GetPagedPosts(page, pageSize);

    if (Request.IsHtmx())
        return PartialView("_BlogSummaryList", posts);

    return View("Index", posts);
}

Questo modello è assolutamente brillante. Una singola azione del controller serve entrambi:

  • Caricamento completo della pagina (quando gli utenti navigano direttamente o aggiornano)
  • Aggiornamenti parziali (quando HTMX fa la richiesta)

Nessun endpoint API separato, nessuna logica duplicata, nessuna serializzazione JSON.

Un errore comune: Molti sviluppatori ASP.NET pensano che è necessario il _ prefisso (come _BlogSummaryList.cshtml) per ottenere il rendering parziale. PartialView() metodo stesso dice ASP.NET Core di saltare il layout - sta dicendo "dimentica il layout, basta renderizzare questo bit." È possibile utilizzare return PartialView("SearchResults", model) con un file di visualizzazione regolare e funziona perfettamente. La convenzione di sottolineatura è nata da pagine web ASP.NET (WebMatrix) dove ha impedito che i file fossero serviti direttamente tramite URL - ma MVC ha sempre protetto tutte le viste dall'accesso diretto comunque. E 'solo una convenzione di denominazione per aiutare a identificare le viste destinati come parziali.

Helper per tag HTMX.NET

HTMX.NET fornisce aiutanti di tag che rendono lavorare con i controller più puliti. Invece di scrivere stringhe di route, è possibile utilizzare riferimenti fortemente digitati:

<button
    hx-controller="Comment"
    hx-action="GetCommentForm"
    hx-post
    hx-target="#commentform">
    Reply
</button>

Questo genera il percorso corretto usando il sistema di routing di ASP.NET Core. Se rinomini il controller o l'azione, il tuo IDE lo catturerà. Molto meglio delle stringhe magiche!

Ecco un vero esempio dal sistema di commento di questo blog:

<button
    class="btn btn-outline btn-sm mb-4"
    hx-action="Comment"
    hx-controller="Comment"
    hx-post
    hx-vals
    x-on:click.prevent="window.mostlylucid.comments.setValues($event)"
    hx-on="htmx:afterSwap: window.scrollTo({top: 0, behavior: 'smooth'})"
    hx-swap="outerHTML"
    hx-target="#commentform">
    Comment
</button>

Notare come HTMX gioca bene con Alpine.js (x-on:click.prevent) per quei bit occasionali di interattività client-side.

Altri helper HTMX.NET

La biblioteca fornisce inoltre:

  • Request.IsHtmxNonBoosted() - Controllare se si tratta di una richiesta HTMX ma non potenziato
  • Request.IsHtmxRefresh() - Controlla se è una richiesta di ripristino della cronologia
  • Helper di risposta per intestazioni HTMX (innesti, reindirizzamenti, ecc.)

Esempio di Real-World: Cerca con Partials

Ecco il controllore di ricerca da questo blog, mostrando il modello di ritorno a tre livelli:

[HttpGet]
[OutputCache(Duration = 3600, VaryByHeaderNames = new[] { "hx-request", "pagerequest" })]
public async Task<IActionResult> Search(
    string? query,
    int page = 1,
    int pageSize = 10,
    [FromHeader] bool pagerequest = false)
{
    var searchModel = await BuildSearchModel(query, page, pageSize);

    if (pagerequest && Request.IsHtmx())
        return PartialView("_SearchResultsPartial", searchModel.SearchResults);

    if (Request.IsHtmx())
        return PartialView("SearchResults", searchModel);

    return View("SearchResults", searchModel);
}

Tre percorsi di ritorno per tre scenari:

  1. Richieste di paginazione - parziale minimo (solo l'elenco dei risultati)
  2. Modifiche del filtro - sezione parziale (risultati con filtri)
  3. Navigazione diretta - pagina intera (layout + tutto)

La vista parziale (_SearchResultsPartial.cshtml) utilizza l'helper del tag paging:

@model Mostlylucid.Models.Blog.PostListViewModel
<div class="pt-2" id="content">
    @if (Model.Data?.Any() is true)
    {
        <div class="inline-flex w-full items-center justify-center pb-4">
            @if (Model.TotalItems > Model.PageSize)
            {
                <pager
                    x-ref="pager"
                    link-url="@Model.LinkUrl"
                    hx-boost="true"
                    hx-target="#content"
                    hx-swap="show:none"
                    page="@Model.Page"
                    page-size="@Model.PageSize"
                    total-items="@Model.TotalItems"
                    hx-headers='{"pagerequest": "true"}'>
                </pager>
            }
        </div>
        @foreach (var post in Model.Data)
        {
            <partial name="_ListPost" model="post"/>
        }
    }
</div>

Spezzare l'helper del tag del pager:

  • hx-boost="true" - Intercetta collegamenti, converte in AJAX
  • hx-target="#content" - Dove iniettare la risposta
  • hx-headers='{"pagerequest": "true"}' - Intestazione personalizzata dice al controller che è paginazione
  • Il controllore controlla Request.IsHtmx() && pagerequest per restituire solo il minimo parziale

Il pacchetto per lo piùlucid.pagingtaghelper

Ho scritto per lo piùlucid.pagingtaghelper per evitare il codice di paginazione ripetitivo. E 'HTMX-primo ma funziona senza JavaScript troppo.

Installazione

dotnet add package mostlylucid.pagingtaghelper

Aggiungi a _ViewImports.cshtml:

@addTagHelper *, mostlylucid.pagingtaghelper

Caratteristiche chiave

Attuazione IPagingModel<T> E hai finito:

public class BasePagingModel<T> : IPagingModel<T> where T : class
{
    public int Page { get; set; }
    public int TotalItems { get; set; }
    public int PageSize { get; set; }
    public string LinkUrl { get; set; }
    public List<T> Data { get; set; }
}

Quello che ottieni

  • Configurazione zero richiesta
  • Framework multipli dell'interfaccia utente (TailwindCSS + DaisyUI, Bootstrap 5, viste personalizzate)
  • Supporto modalità oscura
  • 8 lingue integrate
  • Intestazioni ordinabili, selettori delle dimensioni della pagina
  • Miglioramento progressivo (funziona senza JavaScript)
  • Supporto token di continuazione per database NoSQL

L'helper tag genera collegamenti che mantengono stringhe di query, supportano le intestazioni personalizzate e si integrano senza soluzione di continuità con HTMX (vedi l'esempio sopra).

Diagramma di flusso HTMX

Ecco come si integra l'intero sistema:

graph TB
    A[Browser] -->|"Initial page load"| B[Controller Action]
    B -->|"Request.IsHtmx() = false"| C[Return Full View]
    C --> D[Render Layout + Partial]

    A -->|"User clicks pagination/filter"| E[HTMX Request]
    E -->|"hx-get with headers"| F[Same Controller Action]
    F -->|"Request.IsHtmx() = true"| G{Check Headers}
    G -->|"pagerequest header"| H[Return Minimal Partial]
    G -->|"No special header"| I[Return Section Partial]

    H --> J[Swap Content in Target]
    I --> J

    J -->|"User clicks another link"| E

    style B stroke:#333,stroke-width:2px
    style F stroke:#333,stroke-width:2px
    style C stroke:#0066cc,stroke-width:2px
    style H stroke:#00cc66,stroke-width:2px
    style I stroke:#00cc66,stroke-width:2px

Rispetto ad altri quadri

Frammenti modello Django

Django ha aggiunto un corretto rendering parziale di template nella versione 6.0 (dicembre 2024) con frammenti di template. Prima di allora, gli sviluppatori di Django usavano tipicamente tag di inclusione o pacchetti di terze parti come django-render-block. ASP.NET Core ha avuto PartialView() dalla versione 1.0 nel 2016 - framework diversi, timeline diverse, ma la stessa destinazione: frammenti HTML per HTMX.

Rails Turbo Frames

Ruby on Rails ha Turbo Frames (parte di Hotwire), che è simile nello spirito:

<%= turbo_frame_tag "posts" do %>
  <%= render @posts %>
<% end %>

La differenza è che Turbo richiede specifici frame marker su richiesta e risposta. HTMX è più flessibile - qualsiasi endpoint può restituire qualsiasi HTML, e si decide dove va con hx-target.

Phoenix LiveView

Phoenix LiveView di Elixir adotta un approccio diverso con connessioni WebSocket persistenti e stato lato server:

def handle_event("load_more", _params, socket) do
  {:noreply, assign(socket, posts: load_more_posts())}
end

LiveView è brillante per le applicazioni in tempo reale, ma richiede l'infrastruttura WebSocket e la memoria del server per le connessioni. HTMX utilizza semplice vecchio HTTP - apolide, cacheable, scalabile. Per un blog, questo è perfetto.

Considerazioni sulle prestazioni

Caching di output: La OutputCache attributo varia per hx-request intestazione, caching pagine intere e parziali separatamente:

[OutputCache(Duration = 3600, VaryByHeaderNames = new[] { "hx-request", "pagerequest" })]

Efficienza della rete: Server-rendered HTML è spesso più piccolo di JSON + client-side template, richiede meno viaggi, e cache correttamente.

Dimensione del bundle: HTMX (14KB) + optional Alpine.js (15KB) + paging tag helper (0KB, lato server) = sotto i 30KB totali. Confronta quello con una tipica applicazione React (200KB+).

Modelli avanzati

Aggiornamenti ottimizzati dell'interfaccia utente - Combina HTMX e Alpine per un feedback immediato:

<div x-data="{ count: @Model.CommentCount }">
    <button hx-post="/comment/like" x-on:click="count++" hx-on::after-request="count = $event.detail.xhr.response">
        Likes: <span x-text="count"></span>
    </button>
</div>

Il conteggio aggiorna immediatamente (ottimistico), quindi sincronizza con la risposta del server.

Scambi fuoribordo - Aggiornare più sezioni di pagina da una risposta:

<div id="main-content"><!-- Main response --></div>
<div id="notification-count" hx-swap-oob="true"><span>5 new</span></div>

Perfetto per i badge di notifica, per il conteggio dei carrelli, ecc. Copro questo modello in profondità Mostrando Toast e Scambi con HTMX.

Modelli Client-Side con WebAPI - A volte si desidera un'esperienza intermedia: HTML fornito dal server per la maggior parte delle cose, ma JSON da una WebAPI per specifici contenuti dinamici. estensione client-side-templates ti permette di fare esattamente questo:

<script src="https://unpkg.com/[email protected]/client-side-templates.js"></script>
<script src="https://unpkg.com/mustache@latest"></script>

<div hx-ext="client-side-templates">
    <template id="post-template" type="text/mustache">
        {{#posts}}
        <div class="post">
            <h3>{{title}}</h3>
            <p>{{excerpt}}</p>
        </div>
        {{/posts}}
    </template>

    <button hx-get="/api/posts"
            hx-target="#post-list"
            mustache-template="post-template">
        Load Posts
    </button>
    <div id="post-list"></div>
</div>

Questo approccio funziona con i modelli di Baffetti, Manubri o Nunjucks. Il WebAPI restituisce JSON, ma HTMX gestisce il lato client rendering. È particolarmente utile quando si dispone di un'API esistente o è necessario condividere i dati con le applicazioni mobili. Per maggiori dettagli sulle estensioni HTMX, tra cui client-side-template, vedere l'articolo di accompagnamento.

& Suggerimenti Gotchas

Strumento di debug: Installare il Estensione debug HTMX - mostra ogni richiesta, risposta e scambio in tempo reale.

CSRF TokensCity name (optional, probably does not need a translation)

I token antiforgery di ASP.NET Core necessitano di un trattamento speciale con AJAX. HTMX.NET fornisce diverse opzioni pulite. HtmxAntiforgeryScriptEndpoint:

// In Program.cs
app.MapHtmxAntiforgeryScript();
<!-- In your layout head -->
<script src="@HtmxAntiforgeryScriptEndpoints.Path" defer></script>

O utilizzare l'helper tag: <meta name="htmx-config" includeAspNetAntiforgeryToken="true" />. Vedi L'articolo di Khalid su HTMX Anti-Forgery Tokens per i dettagli completi.

Alpine.js @ Shorthand in Razor

Alpine'sCity name (optional, probably does not need a translation) @click cortocircuito in conflitto con il rasoio @ sintassi. Usare invece la forma esplicita:

<button x-on:click="doSomething()">Click me</button>  <!-- Instead of @click -->
<div x-bind:class="isOpen ? 'block' : 'hidden'"></div>  <!-- Instead of :class -->

O scappare con @@click, ma la sintassi esplicita è più chiara.

Gestione cronologia

Controllo delle voci della cronologia: Per impostazione predefinita, HTMX spinge ogni richiesta alla cronologia. Per pagine/filtri dove non si desidera inquinamento della cronologia:

<paging model="@Model" hx-push-url="false">  <!-- Don't add history entries -->

O usare hx-replace-url="true" per aggiornare l'URL senza aggiungere voci.

Il problema del Fantasma Parziale: Il browser indietro/avanti mostra solo un frammento parziale senza layout. hx-history-elt sul contenitore contenuto:

<div class="container mx-auto" id="contentcontainer" hx-history-elt>
    @RenderBody()
</div>

Questo indica a HTMX quale elemento da istantanea, preservando il layout circostante durante il ripristino della cronologia.

Caching CDN (Cloudflare, ecc.)

Sintoma: HTMX funziona localmente ma si rompe dietro una CDN - contenuti errati viene cached.

Causa principale: I CDN ignorano la HX-Request header. Il tuo server restituisce contenuti diversi in base a questo header, ma il CDN li caches identicamente.

ASP.NET Core fix: Uso VaryByHeaderNames:

[OutputCache(Duration = 3600, VaryByHeaderNames = new[] { "hx-request" })]

Correttore CDN: Configura le regole della cache da includere HX-Request nella chiave cache. Per Cloudflare: Cruscotto → Caching → Regole cache → Chiave cache personalizzata → Intestazioni → Includi HX-Request.

Il dibattito hx-boost

hx-boost converte i collegamenti normali in richieste AJAX. Pagina delle eccellenze HTMX notes some core team members recommend evitarlo (scartamenti <head> contenuto, colpisce la località di comportamento), mentre altri trovano bene per le vittorie rapide.

<div hx-boost="true" hx-target="#contentcontainer">
    <a asp-action="Show" asp-route-slug="@post.Slug">Read More</a>
</div>

Questo blog lo usa ampiamente. Quando si punta a contenitori specifici, esplicito hx-get è più chiaro, ma hx-boost funziona se sei coerente. Disabilita selettivamente con hx-boost="false" sugli elementi per bambini.

Conclusione

HTMX con ASP.NET Core parziales rappresenta un ritorno alla semplicità server-side senza sacrificare UX moderno. Ottieni:

  • Interazioni dinamiche simili alla SPA
  • Rendering lato server (grande per SEO)
  • Caching HTTP corretto
  • JavaScript minimo
  • Miglioramento progressivo
  • Instradamento di tipo sicuro con HTMX.NET
  • Paginazione zero-config con per lo piùlucid.pagingtaghelper

L'approccio basato sul server ha resistito alla prova del tempo, e con HTMX, finalmente ha l'elegante tooling client-side che merita. È possibile costruire applicazioni web robuste e performanti utilizzando modelli che hanno funzionato per decenni - hanno appena aspettato lo strumento giusto per farli brillare di nuovo.

Articoli correlati su questo blog

Articolo "Companion"

Questo articolo fa parte di una serie in due parti su HTMX con ASP.NET Core:

  1. Questo articolo - Si concentra sull'integrazione ASP.NET Core, viste parziali, HTMX.NET, e la paginazione
  2. A Whistle-stop Tour delle estensioni HTMX - Immersione profonda in eventi HTMX, ciclo di vita, architettura di estensione ed estensioni personalizzate

Altri articoli HTMX

Ulteriore lettura

Documentazione ufficiale:

Strumenti e librerie:

Risorse comunitarie:

Finding related posts...
logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.