WebStorage is a simple Blazor WebAssembly (Wasm) Web Storage API wrapper. It implements both session storage and local storage.
| Name | NuGet.org | feedz.io |
|---|---|---|
dotnet add package ForEvolve.Blazor.WebStorage |
There are multiple ways to use WebStorage:
- Use the default
IStorageimplementation. - Use the
IWebStorageAPI that exposes bothLocalStorageandSessionStorage. - Use the
LocalStorageorSessionStorageclasses directly. - Use the
ILocalStorageorISessionStoragemarker interfaces.
To register Web Storage with the IoC container, in your Program.cs file, add the following lines:
usingForEvolve.Blazor.WebStorage;//...builder.Services.AddWebStorage();//...An instance of the LocalStorage class is bound to the IStorage interface by default.
You can inject IStorage in your classes, like this:
publicclassMyService{privatereadonlyIStorage_storage;publicMyService(IStoragestorage){_storage=storage??thrownewArgumentNullException(nameof(storage));}// Omitted implementation}To change the default IStorage binding, you must set the DefaultStorageType property of the WebStorageOptions to StorageType.Session.
Example:
builder.Services.AddWebStorage(options =>options.DefaultStorageType=StorageType.Session);If you want to access both SessionStorage and LocalStorage, you can inject IWebStorage in your classes, like this:
publicclassMyService{privatereadonlyIWebStorage_webStorage;publicMyService(IWebStoragewebStorage){_webStorage=webStorage??thrownewArgumentNullException(nameof(webStorage));}publicvoidSomeMethod(){varlocalStorage=_webStorage.LocalStorage;varsessionStorage=_webStorage.SessionStorage;}}You can also directly inject the LocalStorage and SessionStorage implementation into your classes, like this:
publicclassMyService1{privatereadonlyLocalStorage_localStorage;publicMyService1(LocalStoragelocalStorage){_localStorage=localStorage??thrownewArgumentNullException(nameof(localStorage));}// Omitted implementation}publicclassMyService2{privatereadonlySessionStorage_sessionStorage;publicMyService2(SessionStoragesessionStorage){_sessionStorage=sessionStorage??thrownewArgumentNullException(nameof(sessionStorage));}// Omitted implementation}publicclassMyService3{privatereadonlyLocalStorage_localStorage;privatereadonlySessionStorage_sessionStorage;publicMyService3(LocalStoragelocalStorage,SessionStoragesessionStorage){_localStorage=localStorage??thrownewArgumentNullException(nameof(localStorage));_sessionStorage=sessionStorage??thrownewArgumentNullException(nameof(sessionStorage));}// Omitted implementation}If you don't want to directly inject the implementations, you can leverage the ILocalStorage and ISessionStorage marker interfaces instead.
This yields the same result as injecting the classes directly but allows for more flexibility; you could create decorators or mocks for example.
publicclassMyService1{privatereadonlyILocalStorage_localStorage;publicMyService1(ILocalStoragelocalStorage){_localStorage=localStorage??thrownewArgumentNullException(nameof(localStorage));}// Omitted implementation}publicclassMyService2{privatereadonlyISessionStorage_sessionStorage;publicMyService2(ISessionStoragesessionStorage){_sessionStorage=sessionStorage??thrownewArgumentNullException(nameof(sessionStorage));}// Omitted implementation}publicclassMyService3{privatereadonlyILocalStorage_localStorage;privatereadonlyISessionStorage_sessionStorage;publicMyService3(ILocalStoragelocalStorage,ISessionStoragesessionStorage){_localStorage=localStorage??thrownewArgumentNullException(nameof(localStorage));_sessionStorage=sessionStorage??thrownewArgumentNullException(nameof(sessionStorage));}// Omitted implementation}The IStorage interface implements synchronous and asynchronous versions of the Web Storage JavaScript API.
Both LocalStorage and SessionStorage classes implement the IStorage interface.
publicinterfaceIStorage{/// <summary>/// Gets the number of data items stored in a given <see cref="IStorage"/> object./// </summary>intLength{get;}/// <summary>/// Returns the name of the nth key in a given <see cref="IStorage"/> object./// The order of keys is user-agent defined, so you should not rely on it./// </summary>/// <param name="index">/// The number of the key you want to get the name of./// This is a zero-based index./// </param>/// <returns>/// The name of the key. If the index does not exist, null is returned./// </returns>string?Key(intindex);/// <summary>/// Returns the specified key's value, or null if the key does not exist, in the/// given <see cref="IStorage"/> object./// </summary>/// <param name="keyName">The name of the key you want to retrieve the value of.</param>/// <returns>The value of the key. If the key does not exist, null is returned.</returns>string?GetItem(stringkeyName);/// <summary>/// Adds the specified key to the given <see cref="IStorage"/> object, or/// updates that key's value if it already exists./// </summary>/// <param name="keyName">The name of the key you want to create/update.</param>/// <param name="keyValue">The value you want to give the key you are creating/updating.</param>/// <remarks>/// setItem() may throw an exception if the storage is full. Particularly, in/// Mobile Safari (since iOS 5) it always throws when the user enters private/// mode. (Safari sets the quota to 0 bytes in private mode, unlike other browsers,/// which allow storage in private mode using separate data containers.) Hence/// developers should make sure to always catch possible exceptions from setItem()./// </remarks>voidSetItem(stringkeyName,stringkeyValue);/// <summary>/// Removes the specified key from the given <see cref="IStorage"/> object if it exists./// </summary>/// <param name="keyName">The name of the key you want to remove.</param>voidRemoveItem(stringkeyName);/// <summary>/// Clears all keys stored in a given <see cref="IStorage"/> object./// </summary>voidClear();/// <summary>/// Gets the number of data items stored in a given <see cref="IStorage"/> object./// </summary>/// <param name="cancellationToken"></param>/// <param name="cancellationToken"></param>/// <returns>The number of data items stored in the <see cref="IStorage"/> object.</returns>ValueTask<int>GetLengthAsync(CancellationToken?cancellationToken=default);/// <summary>/// Returns the name of the nth key in a given <see cref="IStorage"/> object./// The order of keys is user-agent defined, so you should not rely on it./// </summary>/// <param name="index">/// The number of the key you want to get the name of./// This is a zero-based index./// </param>/// <param name="cancellationToken"></param>/// <returns>/// The name of the key. If the index does not exist, null is returned./// </returns>ValueTask<string?>KeyAsync(intindex,CancellationToken?cancellationToken=default);/// <summary>/// Returns the specified key's value, or null if the key does not exist, in the/// given <see cref="IStorage"/> object./// </summary>/// <param name="keyName">The name of the key you want to retrieve the value of.</param>/// <param name="cancellationToken"></param>/// <returns>The value of the key. If the key does not exist, null is returned.</returns>ValueTask<string?>GetItemAsync(stringkeyName,CancellationToken?cancellationToken=default);/// <summary>/// Adds the specified key to the given <see cref="IStorage"/> object, or/// updates that key's value if it already exists./// </summary>/// <param name="keyName">The name of the key you want to create/update.</param>/// <param name="keyValue">The value you want to give the key you are creating/updating.</param>/// <param name="cancellationToken"></param>/// <remarks>/// setItem() may throw an exception if the storage is full. Particularly, in/// Mobile Safari (since iOS 5) it always throws when the user enters private/// mode. (Safari sets the quota to 0 bytes in private mode, unlike other browsers,/// which allow storage in private mode using separate data containers.) Hence/// developers should make sure to always catch possible exceptions from setItem()./// </remarks>ValueTaskSetItemAsync(stringkeyName,stringkeyValue,CancellationToken?cancellationToken=default);/// <summary>/// Removes the specified key from the given <see cref="IStorage"/> object if it exists./// </summary>/// <param name="keyName">The name of the key you want to remove.</param>/// <param name="cancellationToken"></param>ValueTaskRemoveItemAsync(stringkeyName,CancellationToken?cancellationToken=default);/// <summary>/// Clears all keys stored in a given <see cref="IStorage"/> object./// </summary>/// <param name="cancellationToken"></param>/// <returns></returns>ValueTaskClearAsync(CancellationToken?cancellationToken=default);}The
IStoragedescriptions are based on the Storage interface documentation written by Mozilla Contributors.
A few IStorage extensions allow serializing and deserializing values automatically, so you can get and set items directly.
The default serializer is JsonWebStorageSerializer which leverage the System.Text.Json.JsonSerializer class.
You can control its behavior by configuring the JsonWebStorageSerializerOptions object using .NET options pattern.
You can implement the IWebStorageSerializer interface to customize the serialization process.
Please open an issue and be as clear as possible; see How to contribute? for more information.
If you would like to contribute to the project, first, thank you for your interest, and please read Contributing to ForEvolve open source projects for more information.
Also, please read the Contributor Covenant Code of Conduct that applies to all ForEvolve repositories.
This project started because I needed access to the storage API in an object with a Singleton lifetime.
So I opened Web Storage API page on MDN, and created this project.
Here are some alternatives that I know about (using a Scoped lifetime):