Sfruttare la local cache del browser tramite gli ETag in ASP.NET Core

di Marco De Sanctis, in ASP.NET Core,

Quando un sito web sfrutta l'output cache, come abbiamo visto finora in ASP.NET Core 7, il server evita di processare la richiesta e fornisce immediatamente la stessa risposta di un'invocazione analoga avvenuta in precedenza.

Si tratta di una grande ottimizzazione per l'uso delle risorse del server, perché la risposta richiede pochissima CPU, ma c'è comunque un transito di dati sulla rete, che può avere costi a livelli di banda e anche di performance.

Una soluzione standard a questo problema è l'uso degli ETag (Entity Tag: https://www.w3.org/2005/MWI/BPWG/techs/CachingWithETag.html), ossia degli identificativi che vengono associati alla risposta del server. Cerchiamo di capire come sfruttarli in ASP.NET Core 7.

Il primo passo è quello di aggiungere l'Etag alla risposta che generiamo:

[HttpGet("demo")]
public IActionResult Cached(string name)
{
    this.HttpContext.Response.Headers.ETag = $"\"{Guid.NewGuid():n}\"";

    return this.Ok($"Hello, {name}, time is {DateTime.Now.ToLongTimeString()} and this is cached");
}

Il tag, come possiamo vedere, è semplicemente un Guid che generiamo randomicamente, e che verrà catturato dal middleware di OutputCache e memorizzato unitamente alla risposta.

A questo punto, immaginiamo di avere un client, per esempio un sito Blazor, in cui invochiamo l'endpoint in alto:

<button class="btn btn-primary" @onclick="FetchDataAsync">Click me</button>

<p>@apiResponse</p>

@code {
    private string apiResponse = "No response";

    private async Task FetchDataAsync()
    {
        apiResponse = await Http.GetStringAsync(
          "https://localhost:7088/api/cache/demo?name=Marco");
    }
}

Come possiamo notare, si tratta di una normalissima richiesta tramite HttpClient, senza alcuna logica di cache.

Quando il browser esegue la richiesta per la prima volta, riceve l'ETag che verrà memorizzato nella sua local cache, unitamente al contenuto della risposta.

HTTP/2 200 OK
content-type: text/plain; charset=utf-8
date: Sun, 05 Mar 2023 11:59:40 GMT
server: Kestrel
access-control-allow-origin: *
etag: "9360fbd21d0d4ec9919a0a0dcd2ba0b9"

A una successiva richiesta, invierà lo stesso ETag in un header If-None-Match, come nel trace in basso:

GET /api/cache/demo?name=Marco HTTP/2
Host: localhost:7088
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/110.0
Accept: */*
Accept-Language: en-GB,en;q=0.5
...
If-None-Match: "9360fbd21d0d4ec9919a0a0dcd2ba0b9"

A questo punto, entra in gioco nuovamente il middleware di OutputCache in ASP.NET Core 7, che confronta questo header con l'ETag in cache, rispondendo con uno status code 304 Not Modified in caso di match, senza inviare alcun contenuto.

HTTP/2 304 Not Modified
date: Sun, 05 Mar 2023 11:59:40 GMT
server: Kestrel
access-control-allow-origin: *
etag: "9360fbd21d0d4ec9919a0a0dcd2ba0b9"
X-Firefox-Spdy: h2

Il risultato serve a segnalare al browser che la sua cache locale è ancora valida, e può essere utilizzata come valore della risposta. La chiamata pertanto avrà una durata pressoché istantanea, come possiamo notare dall'immagine in basso.


Allo scadere della cache sul server, invece, il middleware di OutputCache non riconoscerà più l'ETag inviato dal browser, e risponderà con un 200 OK inviando anche il contenuto della risposta.

Commenti

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Approfondimenti

I più letti di oggi