Local Storage Utility (Cookies) for Blazor WebAssembly App

Here is a local storage utility that relies on dependency injection which means a line of code must be added to the Program.cs file in the Blazor client/WebAssembly app.

Here is one example of using the LocalStorage utility to store an array of strings:

@page "/localstorage"
@inject Utilities.ILocalStorage LocalStorage

<h3>Local Storage</h3>

<div>
@if (data != null)
{
	foreach (var s in data)
	{
	<p>@s</p>
	}
}
</div>

@code {
	string[] data;

	protected override async Task OnInitializedAsync()
	{
		await base.OnInitializedAsync();
		string[] values = new[] { "token", "username", "email", DateTime.Now.ToString() };

		// save array of user data
		await LocalStorage.SaveStringArrayAsync("user", values); // could also use SaveObjectAsync()

		// retrieve array of user data
		data = await LocalStorage.GetStringArrayAsync("user"); // could also use GetObjectAsync()

		// delete user data
		await LocalStorage.RemoveAsync("user");
	}
}

Here is another example of using the LocalStorage utility to store an object:

@page "/localstorage"
@inject Utilities.ILocalStorage LocalStorage
<h3>Local Storage Example</h3>

<div>
    @if (data != null)
    {
        <p>@data.username</p>
        <p>@data.email</p>
        <p>@data.jwt</p>
        <p>@data.created</p>
    }
</div>

@code {
    User data;

    protected override async Task OnInitializedAsync()
    {
        await base.OnInitializedAsync();

        User value = new User { username = "username",
                                email = "email@gmail.com",
                                jwt = "fhasu9f3whfi8fdakid",
                                created = DateTime.Now };

        await LocalStorage.SaveObjectAsync("user", value);

        data = (User)await LocalStorage.GetObjectAsync("user");

        await LocalStorage.RemoveAsync("user");
    }
}
// User.cs
using System;

namespace BlazorExample.Shared
{
    [Serializable]
    public class User
    {
        public string username;
        public string email;
        public string jwt;
        public DateTime created;
    }
}

Here is the code for the LocalStorage utility. It relies on no libraries other than those which are included with Blazor WebAssembly.

// LocalStorage.cs
using System.Threading.Tasks;
using Microsoft.JSInterop;

namespace Utilities
{
	public interface ILocalStorage
	{
		/// <summary>
		/// Remove a key from browser local storage.
		/// </summary>
		/// <param name="key">The key previously used to save to local storage.</param>
		public Task RemoveAsync(string key);

		/// <summary>
		/// Save a string value to browser local storage.
		/// </summary>
		/// <param name="key">The key to use to save to and retrieve from local storage.</param>
		/// <param name="value">The string value to save to local storage.</param>
		public Task SaveStringAsync(string key, string value);

		/// <summary>
		/// Get a string value from browser local storage.
		/// </summary>
		/// <param name="key">The key previously used to save to local storage.</param>
		/// <returns>The string previously saved to local storage.</returns>
		public Task<string> GetStringAsync(string key);

		/// <summary>
		/// Save an array of string values to browser local storage.
		/// </summary>
		/// <param name="key">The key previously used to save to local storage.</param>
		/// <param name="values">The array of string values to save to local storage.</param>
		public Task SaveStringArrayAsync(string key, string[] values);

		/// <summary>
		/// Get an array of string values from browser local storage.
		/// </summary>
		/// <param name="key">The key previously used to save to local storage.</param>
		/// <returns>The array of string values previously saved to local storage.</returns>
		public Task<string[]> GetStringArrayAsync(string key);

		/// <summary>
		/// Save an object value to browser local storage.
		/// </summary>
		/// <param name="key">The key to use to save to and retrieve from local storage.</param>
		/// <param name="value">The object to save to local storage.</param>
		public Task SaveObjectAsync(string key, object value);

		/// <summary>
		/// Get an object from browser local storage.
		/// </summary>
		/// <param name="key">The key previously used to save to local storage.</param>
		/// <returns>The object previously saved to local storage.</returns>
		public Task<object> GetObjectAsync(string key);
	}

	public class LocalStorage : ILocalStorage
	{
		private readonly IJSRuntime jsruntime;
		public LocalStorage(IJSRuntime jSRuntime)
		{
			jsruntime = jSRuntime;
		}

		/// <summary>
		/// Remove a key from browser local storage.
		/// </summary>
		/// <param name="key">The key previously used to save to local storage.</param>
		public async Task RemoveAsync(string key)
		{
			await jsruntime.InvokeVoidAsync("localStorage.removeItem", key).ConfigureAwait(false);
		}

		/// <summary>
		/// Save a string value to browser local storage.
		/// </summary>
		/// <param name="key">The key to use to save to and retrieve from local storage.</param>
		/// <param name="value">The string value to save to local storage.</param>
		public async Task SaveStringAsync(string key, string value)
		{
			await jsruntime.InvokeVoidAsync("localStorage.setItem", key, value).ConfigureAwait(false);
		}

		/// <summary>
		/// Get a string value from browser local storage.
		/// </summary>
		/// <param name="key">The key previously used to save to local storage.</param>
		/// <returns>The string previously saved to local storage.</returns>
		public async Task<string> GetStringAsync(string key)
		{
			return await jsruntime.InvokeAsync<string>("localStorage.getItem", key).ConfigureAwait(false);
		}

		/// <summary>
		/// Save an array of string values to browser local storage.
		/// </summary>
		/// <param name="key">The key previously used to save to local storage.</param>
		/// <param name="values">The array of string values to save to local storage.</param>
		public async Task SaveStringArrayAsync(string key, string[] values)
		{
			if (values != null)
				await jsruntime.InvokeVoidAsync("localStorage.setItem", key, string.Join('\0', values)).ConfigureAwait(false);
		}

		/// <summary>
		/// Get an array of string values from browser local storage.
		/// </summary>
		/// <param name="key">The key previously used to save to local storage.</param>
		/// <returns>The array of string values previously saved to local storage.</returns>
		public async Task<string[]> GetStringArrayAsync(string key)
		{
			var data = await jsruntime.InvokeAsync<string>("localStorage.getItem", key).ConfigureAwait(false);
			if (!string.IsNullOrEmpty(data))
				return data.Split('\0');
			return null;
		}

		/// <summary>
		/// Save an object value to browser local storage.
		/// </summary>
		/// <param name="key">The key to use to save to and retrieve from local storage.</param>
		/// <param name="value">The object to save to local storage.</param>
		public async Task SaveObjectAsync(string key, object value)
		{
			string str = string.Empty;
			var bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
			using (var ms = new System.IO.MemoryStream())
			{
				bf.Serialize(ms, value);
				var bytes = ms.ToArray();
				foreach (var b in bytes)
					str += b.ToString("X2");
			}
			await SaveStringAsync(key, str);
		}

		/// <summary>
		/// Get an object from browser local storage.
		/// </summary>
		/// <param name="key">The key previously used to save to local storage.</param>
		/// <returns>The object previously saved to local storage.</returns>
		public async Task<object> GetObjectAsync(string key)
		{
			try
			{
				string str = await GetStringAsync(key);
				if(str.Length % 2 == 0) // make sure string has an even number of character because one byte takes up two
				{
					var bytes = new byte[str.Length / 2];
					for(int i = 0; i < bytes.Length; i++)
					{
						string val = str.Substring(i * 2, 2);
						bytes[i] = byte.Parse(val, System.Globalization.NumberStyles.HexNumber);
					}
					using (var ms = new System.IO.MemoryStream(bytes))
					{
						var bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
						ms.Position = 0;
						return bf.Deserialize(ms);
					}
				}
			}
			catch { }
			return null;
		}
	}
}

The Progam.cs file must be modified as such (just one line of code is added).

// Program.cs
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.Extensions.DependencyInjection;

namespace BlazorExample.Client
{
	public class Program
	{
		public static async Task Main(string[] args)
		{
			var builder = WebAssemblyHostBuilder.CreateDefault(args);

			// This AddSingleton line of code is for Uilities.LocalStorage to function - notice placement!
			builder.Services.AddSingleton<Utilities.ILocalStorage, Utilities.LocalStorage>();

			builder.RootComponents.Add<App>("app");

			builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });

			await builder.Build().RunAsync();
		}
	}
}

Coding Video

https://youtu.be/iYjpv9yCelo