Skip to content
This repository was archived by the owner on Dec 26, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 50 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@ A library to provide access to session storage in Blazor applications

[![Build Status](https://dev.azure.com/blazored/SessionStorage/_apis/build/status/Blazored.SessionStorage?branchName=master)](https://dev.azure.com/blazored/SessionStorage/_build/latest?definitionId=1&branchName=master)

![Nuget](https://img.shields.io/nuget/v/blazored.sessionstorage.svg)
[![Nuget](https://img.shields.io/nuget/v/blazored.sessionstorage.svg)](https://www.nuget.org/packages/Blazored.SessionStorage/)

### Installing

You can install from NuGet using the following command:

`Install-Package Blazored.SessionStorage`

Or via the Visual Studio package manger.
Or via the Visual Studio package manager.

### Setup

You will need to register the session storage services with the service collection in your _startup.cs_ file.
You will need to register the session storage services with the service collection in your _Startup.cs_ file in Blazor Server.

```c#
public void ConfigureServices(IServiceCollection services)
Expand All @@ -24,6 +24,47 @@ public void ConfigureServices(IServiceCollection services)
}
```

Or in your _Program.cs_ file in Blazor WebAssembly.

```c#
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("app");

builder.Services.AddBlazoredSessionStorage();

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

### Configuration

The session storage provides options that can be modified by you at registration in your _Startup.cs_ file in Blazor Server.


```c#
public void ConfigureServices(IServiceCollection services)
{
services.AddBlazoredSessionStorage(config =>
config.JsonSerializerOptions.WriteIndented = true);
}
```
Or in your _Program.cs_ file in Blazor WebAssembly.

```c#
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("app");

builder.Services.AddBlazoredSessionStorage(config =>
config.JsonSerializerOptions.WriteIndented = true);

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

### Usage (Blazor WebAssembly)
To use Blazored.SessionStorage in Blazor WebAssembly, inject the `ISessionStorageService` per the example below.

Expand Down Expand Up @@ -64,9 +105,9 @@ With Blazor WebAssembly you also have the option of a synchronous API, if your u
```c#
@inject Blazored.SessionStorage.ISessionStorageService sessionStorage

@functions {
@code {

protected override async Task OnAfterRenderAsync()
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await sessionStorage.SetItemAsync("name", "John Smith");
var name = await sessionStorage.GetItemAsync<string>("name");
Expand All @@ -84,12 +125,15 @@ The APIs available are:
- ClearAsync()
- LengthAsync()
- KeyAsync()
- synchronous via `ISyncSessionStorageService`:
- ContainsKeyAsync()

- synchronous via `ISyncSessionStorageService` (Synchronous methods are **only** available in Blazor WebAssembly):
- SetItem()
- GetItem()
- RemoveItem()
- Clear()
- Length()
- Key()
- ContainsKey()

**Note:** Blazored.SessionStorage methods will handle the serialisation and de-serialisation of the data for you.
7 changes: 3 additions & 4 deletions src/Blazored.SessionStorage/Blazored.SessionStorage.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
<TargetFramework>netstandard2.0</TargetFramework>
<RazorLangVersion>3.0</RazorLangVersion>
<RootNamespace>Blazored.SessionStorage</RootNamespace>
<TypeScriptToolsVersion>3.2</TypeScriptToolsVersion>

<PackageId>Blazored.SessionStorage</PackageId>
<Version>1.0.11</Version>
Expand All @@ -18,10 +17,10 @@
<PackageTags>Blazor SessionStorage Blazored Razor Components</PackageTags>
<Company />
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components" Version="3.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="3.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Components" Version="3.1.4" />
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="3.1.4" />
</ItemGroup>

</Project>
2 changes: 1 addition & 1 deletion src/Blazored.SessionStorage/ISessionStorageService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public interface ISessionStorageService

Task RemoveItemAsync(string key);

Task SetItemAsync(string key, object data);
Task SetItemAsync<T>(string key, T data);

event EventHandler<ChangingEventArgs> Changing;
event EventHandler<ChangedEventArgs> Changed;
Expand Down
2 changes: 1 addition & 1 deletion src/Blazored.SessionStorage/ISyncSessionStorageService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public interface ISyncSessionStorageService

void RemoveItem(string key);

void SetItem(string key, object data);
void SetItem<T>(string key, T data);

event EventHandler<ChangingEventArgs> Changing;
event EventHandler<ChangedEventArgs> Changed;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;

namespace Blazored.SessionStorage.JsonConverters
{
/// <summary>
/// The new Json.NET doesn't support Timespan at this time
/// https://github.com/dotnet/corefx/issues/38641
/// </summary>
public class TimespanJsonConverter : JsonConverter<TimeSpan>
{
/// <summary>
/// Format: Days.Hours:Minutes:Seconds:Milliseconds
/// </summary>
public const string TimeSpanFormatString = @"d\.hh\:mm\:ss\:FFF";

public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var s = reader.GetString();
if (string.IsNullOrWhiteSpace(s))
{
return TimeSpan.Zero;
}
else
{
TimeSpan parsedTimeSpan;
if (!TimeSpan.TryParseExact(s, TimeSpanFormatString, null, out parsedTimeSpan))
{
throw new FormatException($"Input timespan is not in an expected format : expected {Regex.Unescape(TimeSpanFormatString)}. Please retrieve this key as a string and parse manually.");
}
else
{
return parsedTimeSpan;
}
}
}

public override void Write(Utf8JsonWriter writer, TimeSpan value, JsonSerializerOptions options)
{
var timespanFormatted = $"{value.ToString(TimeSpanFormatString)}";
writer.WriteStringValue(timespanFormatted);
}
}
}
23 changes: 21 additions & 2 deletions src/Blazored.SessionStorage/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
using Microsoft.Extensions.DependencyInjection;
using System;
using Blazored.SessionStorage.JsonConverters;
using Blazored.SessionStorage.StorageOptions;
using Microsoft.Extensions.DependencyInjection;

namespace Blazored.SessionStorage
{
Expand All @@ -8,7 +11,23 @@ public static IServiceCollection AddBlazoredSessionStorage(this IServiceCollecti
{
return services
.AddScoped<ISessionStorageService, SessionStorageService>()
.AddScoped<ISyncSessionStorageService, SessionStorageService>();
.AddScoped<ISyncSessionStorageService, SessionStorageService>()
.Configure<SessionStorageOptions>(configureOptions =>
{
configureOptions.JsonSerializerOptions.Converters.Add(new TimespanJsonConverter());
});
}

public static IServiceCollection AddBlazoredSessionStorage(this IServiceCollection services, Action<SessionStorageOptions> configure)
{
return services
.AddScoped<ISessionStorageService, SessionStorageService>()
.AddScoped<ISyncSessionStorageService, SessionStorageService>()
.Configure<SessionStorageOptions>(configureOptions =>
{
configure?.Invoke(configureOptions);
configureOptions.JsonSerializerOptions.Converters.Add(new TimespanJsonConverter());
});
}
}
}
49 changes: 29 additions & 20 deletions src/Blazored.SessionStorage/SessionStorageService.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using Microsoft.JSInterop;
using Blazored.SessionStorage.StorageOptions;
using Microsoft.Extensions.Options;
using Microsoft.JSInterop;
using System;
using System.Text.Json;
using System.Threading.Tasks;
Expand All @@ -9,24 +11,31 @@ public class SessionStorageService : ISessionStorageService, ISyncSessionStorage
{
private readonly IJSRuntime _jSRuntime;
private readonly IJSInProcessRuntime _jSInProcessRuntime;
private readonly JsonSerializerOptions _jsonOptions;

public SessionStorageService(IJSRuntime jSRuntime)
public event EventHandler<ChangingEventArgs> Changing;
public event EventHandler<ChangedEventArgs> Changed;

public SessionStorageService(IJSRuntime jSRuntime, IOptions<SessionStorageOptions> options)
{
_jSRuntime = jSRuntime;
_jSInProcessRuntime = jSRuntime as IJSInProcessRuntime;
_jSRuntime = jSRuntime;
_jsonOptions = options.Value.JsonSerializerOptions;
_jSInProcessRuntime = jSRuntime as IJSInProcessRuntime;
}

public async Task SetItemAsync(string key, object data)
public async Task SetItemAsync<T>(string key, T data)
{
if (string.IsNullOrEmpty(key))
throw new ArgumentNullException(nameof(key));

var e = await RaiseOnChangingAsync(key, data);

if (e.Cancel)
return;

await _jSRuntime.InvokeAsync<object>("sessionStorage.setItem", key, JsonSerializer.Serialize(data));
return;

var serialisedData = JsonSerializer.Serialize(data, _jsonOptions);

await _jSRuntime.InvokeVoidAsync("sessionStorage.setItem", key, serialisedData);

RaiseOnChanged(key, e.OldValue, data);
}
Expand All @@ -39,9 +48,9 @@ public async Task<T> GetItemAsync<T>(string key)
var serialisedData = await _jSRuntime.InvokeAsync<string>("sessionStorage.getItem", key);

if (serialisedData == null)
return default(T);

return JsonSerializer.Deserialize<T>(serialisedData);
return default;

return JsonSerializer.Deserialize<T>(serialisedData, _jsonOptions);
}

public async Task RemoveItemAsync(string key)
Expand All @@ -58,7 +67,7 @@ public async Task RemoveItemAsync(string key)

public async Task<string> KeyAsync(int index) => await _jSRuntime.InvokeAsync<string>("sessionStorage.key", index);

public void SetItem(string key, object data)
public void SetItem<T>(string key, T data)
{
if (string.IsNullOrEmpty(key))
throw new ArgumentNullException(nameof(key));
Expand All @@ -71,7 +80,9 @@ public void SetItem(string key, object data)
if (e.Cancel)
return;

_jSInProcessRuntime.Invoke<object>("sessionStorage.setItem", key, JsonSerializer.Serialize(data));
var serialisedData = JsonSerializer.Serialize(data, _jsonOptions);

_jSInProcessRuntime.InvokeVoid("sessionStorage.setItem", key, serialisedData);

RaiseOnChanged(key, e.OldValue, data);
}
Expand All @@ -87,9 +98,9 @@ public T GetItem<T>(string key)
var serialisedData = _jSInProcessRuntime.Invoke<string>("sessionStorage.getItem", key);

if (serialisedData == null)
return default(T);

return JsonSerializer.Deserialize<T>(serialisedData);
return default;

return JsonSerializer.Deserialize<T>(serialisedData, _jsonOptions);
}

public void RemoveItem(string key)
Expand All @@ -100,15 +111,15 @@ public void RemoveItem(string key)
if (_jSInProcessRuntime == null)
throw new InvalidOperationException("IJSInProcessRuntime not available");

_jSInProcessRuntime.Invoke<object>("sessionStorage.removeItem", key);
_jSInProcessRuntime.InvokeVoid("sessionStorage.removeItem", key);
}

public void Clear()
{
if (_jSInProcessRuntime == null)
throw new InvalidOperationException("IJSInProcessRuntime not available");

_jSInProcessRuntime.Invoke<object>("sessionStorage.clear");
_jSInProcessRuntime.InvokeVoid("sessionStorage.clear");
}

public int Length()
Expand All @@ -127,7 +138,6 @@ public string Key(int index)
return _jSInProcessRuntime.Invoke<string>("sessionStorage.key", index);
}

public event EventHandler<ChangingEventArgs> Changing;
private async Task<ChangingEventArgs> RaiseOnChangingAsync(string key, object data)
{
var e = new ChangingEventArgs
Expand Down Expand Up @@ -156,7 +166,6 @@ private ChangingEventArgs RaiseOnChangingSync(string key, object data)
return e;
}

public event EventHandler<ChangedEventArgs> Changed;
private void RaiseOnChanged(string key, object oldValue, object data)
{
var e = new ChangedEventArgs
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System.Text.Json;

namespace Blazored.SessionStorage.StorageOptions
{
public class SessionStorageOptions
{
public JsonSerializerOptions JsonSerializerOptions { get; } = new JsonSerializerOptions
{
DictionaryKeyPolicy = JsonNamingPolicy.CamelCase,
IgnoreNullValues = true,
IgnoreReadOnlyProperties = true,
PropertyNameCaseInsensitive = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
ReadCommentHandling = JsonCommentHandling.Skip,
WriteIndented = false
};
}
}