Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Catch errors when getting from browser storage #7001

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
31 changes: 25 additions & 6 deletions src/Aspire.Dashboard/Model/BrowserStorage/BrowserStorageBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,41 @@

namespace Aspire.Dashboard.Model.BrowserStorage;

public abstract class BrowserStorageBase : IBrowserStorage
public abstract class BrowserStorageBase<T> : IBrowserStorage where T : BrowserStorageBase<T>
{
private readonly ProtectedBrowserStorage _protectedBrowserStorage;

protected BrowserStorageBase(ProtectedBrowserStorage protectedBrowserStorage)
protected BrowserStorageBase(ProtectedBrowserStorage protectedBrowserStorage, ILogger<T> logger)
{
_protectedBrowserStorage = protectedBrowserStorage;
Logger = logger;
}

public async Task<StorageResult<T>> GetAsync<T>(string key)
public ILogger<T> Logger { get; }

public async Task<StorageResult<TValue>> GetAsync<TValue>(string key)
{
var result = await _protectedBrowserStorage.GetAsync<T>(key).ConfigureAwait(false);
return new StorageResult<T>(result.Success, result.Value);
try
{
// Possible errors here:
// - Saved value in storage can't be deserialized to TValue.
// - Saved value has a different data protection key than the current one.
// This could happen with values saved to persistent browser and the user upgrades Aspire version, which has a different
// install location and so a different data protection key.
// It could also be caused by standalone dashboard, which creates a new key each run. Leaving the dashboard browser open
// while restarting the container will cause a new data protection key, even with session storage.
var result = await _protectedBrowserStorage.GetAsync<TValue>(key).ConfigureAwait(false);
return new StorageResult<TValue>(result.Success, result.Value);
}
catch (Exception ex)
{
Logger.LogWarning(ex, "Error when reading '{Key}' as {ValueType}.", key, typeof(TValue).Name);

return new StorageResult<TValue>(false, default);
}
}

public async Task SetAsync<T>(string key, T value)
public async Task SetAsync<TValue>(string key, TValue value)
{
await _protectedBrowserStorage.SetAsync(key, value!).ConfigureAwait(false);
}
Expand Down
4 changes: 2 additions & 2 deletions src/Aspire.Dashboard/Model/BrowserStorage/IBrowserStorage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ namespace Aspire.Dashboard.Model.BrowserStorage;

public interface IBrowserStorage
{
Task<StorageResult<T>> GetAsync<T>(string key);
Task SetAsync<T>(string key, T value);
Task<StorageResult<TValue>> GetAsync<TValue>(string key);
Task SetAsync<TValue>(string key, TValue value);
}
4 changes: 2 additions & 2 deletions src/Aspire.Dashboard/Model/BrowserStorage/ILocalStorage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ public interface ILocalStorage : IBrowserStorage
/// <summary>
/// Get unprotected data from local storage. This must only be used with non-sensitive data.
/// </summary>
Task<StorageResult<T>> GetUnprotectedAsync<T>(string key);
Task<StorageResult<TValue>> GetUnprotectedAsync<TValue>(string key);

/// <summary>
/// Set unprotected data to local storage. This must only be used with non-sensitive data.
/// </summary>
Task SetUnprotectedAsync<T>(string key, T value);
Task SetUnprotectedAsync<TValue>(string key, TValue value);
}
18 changes: 8 additions & 10 deletions src/Aspire.Dashboard/Model/BrowserStorage/LocalBrowserStorage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace Aspire.Dashboard.Model.BrowserStorage;

public class LocalBrowserStorage : BrowserStorageBase, ILocalStorage
public class LocalBrowserStorage : BrowserStorageBase<LocalBrowserStorage>, ILocalStorage
{
private static readonly JsonSerializerOptions s_options = new()
{
Expand All @@ -16,36 +16,34 @@ public class LocalBrowserStorage : BrowserStorageBase, ILocalStorage
};

private readonly IJSRuntime _jsRuntime;
private readonly ILogger<LocalBrowserStorage> _logger;

public LocalBrowserStorage(IJSRuntime jsRuntime, ProtectedLocalStorage protectedLocalStorage, ILogger<LocalBrowserStorage> logger) : base(protectedLocalStorage)
public LocalBrowserStorage(IJSRuntime jsRuntime, ProtectedLocalStorage protectedLocalStorage, ILogger<LocalBrowserStorage> logger) : base(protectedLocalStorage, logger)
{
_jsRuntime = jsRuntime;
_logger = logger;
}

public async Task<StorageResult<T>> GetUnprotectedAsync<T>(string key)
public async Task<StorageResult<TValue>> GetUnprotectedAsync<TValue>(string key)
{
var json = await GetJsonAsync(key).ConfigureAwait(false);

if (json == null)
{
return new StorageResult<T>(false, default);
return new StorageResult<TValue>(false, default);
}

try
{
return new StorageResult<T>(true, JsonSerializer.Deserialize<T>(json, s_options));
return new StorageResult<TValue>(true, JsonSerializer.Deserialize<TValue>(json, s_options));
}
catch (Exception ex)
{
_logger.LogWarning(ex, $"Error when reading '{key}' as {typeof(T).Name} from local browser storage.");
Logger.LogWarning(ex, $"Error when reading '{key}' as {typeof(TValue).Name} from local browser storage.");

return new StorageResult<T>(false, default);
return new StorageResult<TValue>(false, default);
}
}

public async Task SetUnprotectedAsync<T>(string key, T value)
public async Task SetUnprotectedAsync<TValue>(string key, TValue value)
{
var json = JsonSerializer.Serialize(value, s_options);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@

namespace Aspire.Dashboard.Model.BrowserStorage;

public class SessionBrowserStorage : BrowserStorageBase, ISessionStorage
public class SessionBrowserStorage : BrowserStorageBase<SessionBrowserStorage>, ISessionStorage
{
public SessionBrowserStorage(ProtectedSessionStorage protectedSessionStorage) : base(protectedSessionStorage)
public SessionBrowserStorage(ProtectedSessionStorage protectedSessionStorage, ILogger<SessionBrowserStorage> logger) : base(protectedSessionStorage, logger)
{
}
}
4 changes: 2 additions & 2 deletions src/Aspire.Dashboard/Model/BrowserStorage/StorageResult.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Aspire.Dashboard.Model.BrowserStorage;

public readonly record struct StorageResult<T>(bool Success, T? Value);
public readonly record struct StorageResult<TValue>(bool Success, TValue? Value);
Loading