Cache storage

🟨 Slightly different to Blazor Server

When loading web pages, the browser often stores responses in cache to speed up the process. This is particularly common for caching JavaScript files. As a developer, you typically have no control over this caching process. However, with Cache storage, you can store requests and responses that you initiated and use them to reduce calls to the server or to provide offline support.

This tutorial provides a basic setup for interacting with Cache storage, which can be extended with additional features as needed. By following this tutorial, you will learn:

  • What is Cache storage?
  • Set up the base code for interacting with Cache storage.
  • Add common operations to your Cache storage code.
  • Use Cache storage.
  • Key differences between Blazor WebAssembly and Blazor Server.
You can download the example code used in this topic on GitHub.

What is Cache storage?

Cache storage is an excellent option for caching URL-addressable resources. It is particularly useful when dealing with large amounts of data or when users need to access the website without an internet connection. However, it's worth noting that Cache storage is a relatively new technology, so it's important to check whether the browser supports it before implementation. The Cache storage only accepts Request and Response as key and value, respectively.

What is Request and Response?

Request and Response are two JavaScript classes used to handle API requests and responses. As the name implies, Request represents a request to an API, while Response represents a response from the API. These classes are used to construct and parse requests and responses, and to interact with the data that is returned from the API.

How to access the Cache storage in the browser?

In Firefox, you can find the Cache storage under the Storage tab, as shown in the image below:

firefox-cache-storage.png

In Chrome and Edge, you can find the Cache storage under the Application tab, as shown in the images below:

chrome-cache-storage.png

edge-cache-storage.png


Set up the base code for interacting with Cache storage

The Cache storage is exclusively accessible through the JavaScript API. This means that in order to access it, you will need to create a JavaScript module. You can refer to the JavaScript Interaction tutorial and choose between IJSRuntime or InteropServices.JavaScript to interact with your module. Below is an example of how to access the cache storage using IJSRuntime.

  1. Create a new JavaScript file under the wwwroot folder. For example:

/js/CacheStorageAccessor.js

async function openCacheStorage()
{
    return await window.caches.open("Blazor School");
}

function createRequest(url, method, body = "")
{
    let requestInit =
    {
        method: method
    };

    if (body != "")
    {
        requestInit.body = body;
    }

    let request = new Request(url, requestInit);

    return request;
}
  1. Create a C# class with the following base implementation:
public class CacheStorageAccessor : IAsyncDisposable
{
    private Lazy<IJSObjectReference> _accessorJsRef = new();
    private readonly IJSRuntime _jsRuntime;

    public CacheStorageAccessor(IJSRuntime jsRuntime)
    {
        _jsRuntime = jsRuntime;
    }

    private async Task WaitForReference()
    {
        if (_accessorJsRef.IsValueCreated is false)
        {
            _accessorJsRef = new(await _jsRuntime.InvokeAsync<IJSObjectReference>("import", "/js/CacheStorageAccessor.js"));
        }
    }

    public async ValueTask DisposeAsync()
    {
        if (_accessorJsRef.IsValueCreated)
        {
            await _accessorJsRef.Value.DisposeAsync();
        }
    }
}
  1. Register the CacheStorageAccessor class in the Program.cs file:
builder.Services.AddScoped<CacheStorageAccessor>();

Add common operations to your Cache storage code

In this section, we will demonstrate how to perform basic Cache storage operations in JavaScript and C#. With the provided code, you can easily store, get, and delete data from the cache. Additionally, you can customize the code to add your own operations as well.

  1. To store, get, or delete data, add the following functions to your JavaScript module:
export async function store(url, method, body = "", responseString)
{
    let blazorSchoolCache = await openCacheStorage();
    let request = createRequest(url, method, body);
    let response = new Response(responseString);
    await blazorSchoolCache.put(request, response);
}

export async function get(url, method, body = "")
{
    let blazorSchoolCache = await openCacheStorage();
    let request = createRequest(url, method, body);
    let response = await blazorSchoolCache.match(request);

    if (response == undefined)
    {
        return "";
    }

    let result = await response.text();

    return result;
}

export async function remove(url, method, body = "")
{
    let blazorSchoolCache = await openCacheStorage();
    let request = createRequest(url, method, body);
    await blazorSchoolCache.delete(request);
}

export async function removeAll()
{
    let blazorSchoolCache = await openCacheStorage();
    let requests = await blazorSchoolCache.keys();

    for (let i = 0; i < requests.length; i++)
    {
        await blazorSchoolCache.delete(requests[i]);
    }
}
  1. To use these operations in your C# class, add a method for each operation, and call WaitForReference() in all of them. Here is an example implementation:
public class CacheStorageAccessor : IAsyncDisposable
{
    ...
    public async Task StoreAsync(HttpRequestMessage requestMessage, HttpResponseMessage responseMessage)
    {
        await WaitForReference();
        string requestMethod = requestMessage.Method.Method;
        string requestBody = await GetRequestBodyAsync(requestMessage);
        string responseBody = await responseMessage.Content.ReadAsStringAsync();

        await _accessorJsRef.Value.InvokeVoidAsync("store", requestMessage.RequestUri, requestMethod, requestBody, responseBody);
    }

    public async Task<string> GetAsync(HttpRequestMessage requestMessage)
    {
        await WaitForReference();
        string requestMethod = requestMessage.Method.Method;
        string requestBody = await GetRequestBodyAsync(requestMessage);
        string result = await _accessorJsRef.Value.InvokeAsync<string>("get", requestMessage.RequestUri, requestMethod, requestBody);

        return result;
    }

    public async Task RemoveAsync(HttpRequestMessage requestMessage)
    {
        await WaitForReference();
        string requestMethod = requestMessage.Method.Method;
        string requestBody = await GetRequestBodyAsync(requestMessage);
        await _accessorJsRef.Value.InvokeVoidAsync("remove", requestMessage.RequestUri, requestMethod, requestBody);
    }

    public async Task RemoveAllAsync()
    {
        await WaitForReference();
        await _accessorJsRef.Value.InvokeVoidAsync("removeAll");
    }

    private static async Task<string> GetRequestBodyAsync(HttpRequestMessage requestMessage)
    {
        string requestBody = "";

        if (requestMessage.Content is not null)
        {
            requestBody = await requestMessage.Content.ReadAsStringAsync() ?? "";
        }

        return requestBody;
    }
}

Use Cache storage

Once you have completed all the previous steps, you can use the Cache storage as follows:

@inject CacheStorageAccessor CacheStorageAccessor
@inject HttpClient HttpClient

<button type="button" @onclick="SetValueAsync">Set Value</button>
<div>Stored Value: @StoredValue</div>
<button type="button" @onclick="GetValueAsync">Get Value</button>
<button type="button" @onclick="RemoveAsync">Remove Value</button>
<button type="button" @onclick="ClearAllAsync">Clear All</button>

@code {
    public string StoredValue { get; set; } = "";

    public async Task SetValueAsync()
    {
        var message = CreateMessage();
        var response = await HttpClient.SendAsync(message);
        await CacheStorageAccessor.StoreAsync(message, response);
    }

    public async Task GetValueAsync()
    {
        StoredValue = await CacheStorageAccessor.GetAsync(CreateMessage());
    }

    public async Task RemoveAsync()
    {
        await CacheStorageAccessor.RemoveAsync(CreateMessage());
    }

    public async Task ClearAllAsync()
    {
        await CacheStorageAccessor.RemoveAllAsync();
    }

    public HttpRequestMessage CreateMessage() => new HttpRequestMessage(HttpMethod.Get, "/sample-data/books.json");
}
Note that you need to create a new HttpRequestMessage every time you want to store a new request.

The example code above shows how to store, retrieve, remove, and clear all stored values using the Cache Storage.


Key differences between Blazor WebAssembly and Blazor Server

In Blazor Server, you can only use IJSRuntime to interact with the JavaScript.

BLAZOR SCHOOL
Designed and built with care by our dedicated team, with contributions from a supportive community. We strive to provide the best learning experience for our users.
Docs licensed CC-BY-SA-4.0
Copyright © 2021-2024 Blazor School
An unhandled error has occurred. Reload 🗙