Blazor WASM - Cache Storage Using JavaScript

Browser Storage

Storing something in the browser to access it easier & frequently with less load time.

Here is a very interesting article on how to store Encrypted Session Data in the browser.

Types of browser storage

There are 6 types of browser storage:

  • Cache
  • Cookie
  • Indexed DB
  • Local storage
  • Memory
  • Session storage

Compare browser storage types

  Its Lifetime Allowed data type Shared between browser tabs
Cache Until deleted Request, Response YES
Cookie Expired time / until deleted Key value YES
Indexed DB Until deleted Various types YES
Local storage Until deleted Key value YES
Memory Users exist/ close browser tab Various types NO
Session storage Users exist/ close browser tab Key value NO

In this article, we are going to see about cache storage and how to implement this in Blazor WASM using JavaScript.

Cache Storage

Store any request/responses as cache in browser to speed up the loading process.

Benefits

  • Improves performance
  • Reduce call to servers
  • Provide offline data support

Implementation

Workflow

  • We are having weather.json in wwwroot folder, so consider this as response.
  • Now we want to store that JSON response in browser as cache and access it with our code.
  • We are going to do the following basic operation like store, get, remove, delete with cache data

Step 1 - Base code setup

Have following files in following path as shown below

Step 2

Create razor page named CacheStorage.razor and add following code to activate cache storage and its operations

@page "/cache"
@inject MyBlazorWasmApp.Helper.CacheStorageAccessor CacheStorageAccessor
@inject HttpClient HttpClient
<h3>CacheStorage</h3>
<hr />
<button class="btn btn-primary" type="button" @onclick="SetValueAsync">Set Value</button>
<div>Stored Value: @StoredValue</div>
<button class="btn btn-primary" type="button" @onclick="GetValueAsync">Get Value</button>
<button class="btn btn-primary" type="button" @onclick="RemoveAsync">Remove Value</button>
<button class="btn btn-primary" 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/weather.json");
}

The razor page will call respective JavaScript function to process cache

Step 3

Setup JavaScript file with below code

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

function createRequest(url, method, body = "") {
    let requestInit = {
        method: method
    };
    if (body != "") {
        requestInit.body = body;
    }
    let request = new Request(url, requestInit);
    console.log(request);
    return request;
}
//In your JavaScript module, add functions to store, get, delete the data:
export async function store(url, method, body = "", responseString) {
    let kajulBlazorCache = await openCacheStorage();
    let request = createRequest(url, method, body);
    let response = new Response(responseString);
    await kajulBlazorCache.put(request, response);
}
export async function get(url, method, body = "") {
    let kajulBlazorCache = await openCacheStorage();
    let request = createRequest(url, method, body);
    let response = await kajulBlazorCache.match(request);
    if (response == undefined) {
        return "";
    }
    let result = await response.text();
    return result;
}
export async function remove(url, method, body = "") {
    let kajulBlazorCache = await openCacheStorage();
    let request = createRequest(url, method, body);
    await kajulBlazorCache.delete(request);
}
export async function removeAll() {
    let kajulBlazorCache = await openCacheStorage();
    let requests = await kajulBlazorCache.keys();
    for (let i = 0; i < requests.length; i++) {
        await kajulBlazorCache.delete(requests[i]);
    }
}

Step 4

Create a helper class (CacheStorageAccessor.cs) to connect razor and JavaScript functions

using Microsoft.JSInterop;
namespace MyBlazorWasmApp.Helper;
public class CacheStorageAccessor: IAsyncDisposable {
    private Lazy < IJSObjectReference > _accessorJsRef = new();
    private readonly IJSRuntime _jsRuntime;
    //constructor
    public CacheStorageAccessor(IJSRuntime jsRuntime) {
        _jsRuntime = jsRuntime;
    }
    //Common method - You will need to call WaitForReference() in all methods.
    private async Task WaitForReference() {
        if (_accessorJsRef.IsValueCreated is false) {
            _accessorJsRef = new(await _jsRuntime.InvokeAsync < IJSObjectReference > ("import", "/js/CacheStorageAccessor.js"));
        }
    }
    //Always remember to dispose the JavaScript module.
    public async ValueTask DisposeAsync() {
        if (_accessorJsRef.IsValueCreated) {
            await _accessorJsRef.Value.DisposeAsync();
        }
    }
    #region create a new method
    for each operation
    //the below is C# blazor methods will link the js functions respective to its name
    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;
    }
    #endregion
}

Step 5

Register the helper class in Program.cs

using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using MyBlazorWasmApp;
using MyBlazorWasmApp.Helper;
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add < App > ("#app");
builder.RootComponents.Add < HeadOutlet > ("head::after");
//Register helper class
builder.Services.AddScoped < CacheStorageAccessor > ();
builder.Services.AddScoped(sp => new HttpClient {
    BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)
});
await builder.Build().RunAsync();

Step 6: Run to See output in browser

Output for blazar wasm cache storage using js

output

*** !!! Happy coding !!! ***

Check out Blazor on C# Corner to learn more about Blazor.


Similar Articles