Added SessionState Components

This commit is contained in:
RDeck 2025-05-15 06:12:34 -04:00
parent 36ca8b9cc3
commit 3e6d6dc96e
26 changed files with 711 additions and 45 deletions

Binary file not shown.

View File

@ -1,7 +1,48 @@
{
"Version": 1,
"WorkspaceRootPath": "C:\\Repos\\SummerBestWebForm2\\",
"Documents": [],
"Documents": [
{
"AbsoluteMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|c:\\repos\\summerbestwebform2\\components\\app.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}",
"RelativeMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|solutionrelative:components\\app.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}"
},
{
"AbsoluteMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|c:\\repos\\summerbestwebform2\\components\\_imports.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}",
"RelativeMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|solutionrelative:components\\_imports.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}"
},
{
"AbsoluteMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|c:\\repos\\summerbestwebform2\\sessionstate\\sessionmanager.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|solutionrelative:sessionstate\\sessionmanager.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|c:\\repos\\summerbestwebform2\\sessionstate\\sessionidmanager.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|solutionrelative:sessionstate\\sessionidmanager.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|c:\\repos\\summerbestwebform2\\classobj\\mdlsessioninfo.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|solutionrelative:classobj\\mdlsessioninfo.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|c:\\repos\\summerbestwebform2\\sessionstate\\mdlsession.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|solutionrelative:sessionstate\\mdlsession.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|c:\\repos\\summerbestwebform2\\appstate\\mdlappstate.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|solutionrelative:appstate\\mdlappstate.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|c:\\repos\\summerbestwebform2\\appstate\\cascadingappstate.razor.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|solutionrelative:appstate\\cascadingappstate.razor.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|c:\\repos\\summerbestwebform2\\appstate\\iappstate.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|solutionrelative:appstate\\iappstate.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|c:\\repos\\summerbestwebform2\\program.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|solutionrelative:program.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
}
],
"DocumentGroupContainers": [
{
"Orientation": 0,
@ -9,7 +50,7 @@
"DocumentGroups": [
{
"DockedWidth": 200,
"SelectedChildIndex": -1,
"SelectedChildIndex": 11,
"Children": [
{
"$type": "Bookmark",
@ -54,6 +95,136 @@
{
"$type": "Bookmark",
"Name": "ST:0:0:{aa2115a1-9712-457b-9047-dbb71ca2cdd2}"
},
{
"$type": "Document",
"DocumentIndex": 0,
"Title": "App.razor",
"DocumentMoniker": "C:\\Repos\\SummerBestWebForm2\\Components\\App.razor",
"RelativeDocumentMoniker": "Components\\App.razor",
"ToolTip": "C:\\Repos\\SummerBestWebForm2\\Components\\App.razor",
"RelativeToolTip": "Components\\App.razor",
"ViewState": "AgIAACQAAAAAAAAAAAAYwFwAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
"WhenOpened": "2025-05-15T09:23:12.142Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 9,
"Title": "Program.cs",
"DocumentMoniker": "C:\\Repos\\SummerBestWebForm2\\Program.cs",
"RelativeDocumentMoniker": "Program.cs",
"ToolTip": "C:\\Repos\\SummerBestWebForm2\\Program.cs",
"RelativeToolTip": "Program.cs",
"ViewState": "AgIAABMAAAAAAAAAAAAAACsAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-05-15T10:01:38.124Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 8,
"Title": "IAppState.cs",
"DocumentMoniker": "C:\\Repos\\SummerBestWebForm2\\AppState\\IAppState.cs",
"RelativeDocumentMoniker": "AppState\\IAppState.cs",
"ToolTip": "C:\\Repos\\SummerBestWebForm2\\AppState\\IAppState.cs",
"RelativeToolTip": "AppState\\IAppState.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAKAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-05-15T10:03:35.861Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 7,
"Title": "CascadingAppState.razor.cs",
"DocumentMoniker": "C:\\Repos\\SummerBestWebForm2\\AppState\\CascadingAppState.razor.cs",
"RelativeDocumentMoniker": "AppState\\CascadingAppState.razor.cs",
"ToolTip": "C:\\Repos\\SummerBestWebForm2\\AppState\\CascadingAppState.razor.cs",
"RelativeToolTip": "AppState\\CascadingAppState.razor.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAoAAAA6AAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-05-15T10:03:51.372Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 6,
"Title": "MdlAppState.cs",
"DocumentMoniker": "C:\\Repos\\SummerBestWebForm2\\AppState\\MdlAppState.cs",
"RelativeDocumentMoniker": "AppState\\MdlAppState.cs",
"ToolTip": "C:\\Repos\\SummerBestWebForm2\\AppState\\MdlAppState.cs",
"RelativeToolTip": "AppState\\MdlAppState.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAcAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-05-15T10:04:02.184Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 5,
"Title": "MdlSession.cs",
"DocumentMoniker": "C:\\Repos\\SummerBestWebForm2\\SessionState\\MdlSession.cs",
"RelativeDocumentMoniker": "SessionState\\MdlSession.cs",
"ToolTip": "C:\\Repos\\SummerBestWebForm2\\SessionState\\MdlSession.cs",
"RelativeToolTip": "SessionState\\MdlSession.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAcAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-05-15T10:04:06.197Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 4,
"Title": "MdlSessionInfo.cs",
"DocumentMoniker": "C:\\Repos\\SummerBestWebForm2\\ClassObj\\MdlSessionInfo.cs",
"RelativeDocumentMoniker": "ClassObj\\MdlSessionInfo.cs",
"ToolTip": "C:\\Repos\\SummerBestWebForm2\\ClassObj\\MdlSessionInfo.cs",
"RelativeToolTip": "ClassObj\\MdlSessionInfo.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAcAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-05-15T10:04:10.974Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 3,
"Title": "SessionIdManager.cs",
"DocumentMoniker": "C:\\Repos\\SummerBestWebForm2\\SessionState\\SessionIdManager.cs",
"RelativeDocumentMoniker": "SessionState\\SessionIdManager.cs",
"ToolTip": "C:\\Repos\\SummerBestWebForm2\\SessionState\\SessionIdManager.cs",
"RelativeToolTip": "SessionState\\SessionIdManager.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAcAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-05-15T10:04:14.473Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 2,
"Title": "SessionManager.cs",
"DocumentMoniker": "C:\\Repos\\SummerBestWebForm2\\SessionState\\SessionManager.cs",
"RelativeDocumentMoniker": "SessionState\\SessionManager.cs",
"ToolTip": "C:\\Repos\\SummerBestWebForm2\\SessionState\\SessionManager.cs",
"RelativeToolTip": "SessionState\\SessionManager.cs",
"ViewState": "AgIAAAIAAAAAAAAAAAAAAAIAAAAcAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-05-15T10:04:17.539Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 1,
"Title": "_Imports.razor",
"DocumentMoniker": "C:\\Repos\\SummerBestWebForm2\\Components\\_Imports.razor",
"RelativeDocumentMoniker": "Components\\_Imports.razor",
"ToolTip": "C:\\Repos\\SummerBestWebForm2\\Components\\_Imports.razor",
"RelativeToolTip": "Components\\_Imports.razor",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAoAAAAiAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
"WhenOpened": "2025-05-15T10:07:58.031Z",
"EditorCaption": ""
}
]
}

View File

@ -0,0 +1,7 @@
@* @using static Microsoft.AspNetCore.Components.Web.RenderMode
@rendermode InteractiveServer
*@
<CascadingValue Value="this">
@ChildContent
</CascadingValue>

View File

@ -0,0 +1,154 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage;
using System.Reflection;
using System.Text.Json;
using System.Text.RegularExpressions;
namespace SummerBestWebForm2.AppState;
public partial class CascadingAppState : ComponentBase, IAppState
{
private readonly string StorageKey = "SummerBestWebForm2-MyCarHQ";
[Parameter]
public RenderFragment? ChildContent { get; set; }
[Inject]
ProtectedLocalStorage? localStorage { get; set; }
[Inject]
IHttpContextAccessor? httpContextAccessor { get; set; }
bool isLoaded = false;
public Guid SessionId { get; set; }
public bool isInit { get; set; }
//public CascadingAppState()
//{
// Load();
//}
private string? _myIpAddress = string.Empty;
public string? myIpAddress
{
get => _myIpAddress;
set
{
_myIpAddress = value;
Save();
}
}
private DateTimeOffset _DateCreated = DateTimeOffset.Now;
public DateTimeOffset DateCreated
{
get => _DateCreated;
set
{
_DateCreated = value;
Save();
}
}
private DateTimeOffset _DateExpires = DateTimeOffset.Now;
public DateTimeOffset DateExpires
{
get => _DateExpires;
set
{
_DateExpires = value;
Save();
}
}
// (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O)
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnAfterRenderAsync(firstRender);
if (firstRender)
{
await LoadAsync();
StateHasChanged();
}
}
private void Save()
{
new Task(async () =>
{
await SaveAsync();
}).Start();
} //Save
public async Task SaveAsync()
{
if (!isLoaded) return;
// serialize
var state = (IAppState)this;
var json = JsonSerializer.Serialize(state);
// save
await localStorage.SetAsync(StorageKey, json);
} //SaveAsync
private void Load()
{
new Task(async () =>
{
await LoadAsync();
}).Start();
} //Load
public async Task LoadAsync()
{
string remoteIpAddr=string.Empty;
try
{
remoteIpAddr = httpContextAccessor.HttpContext.Connection.RemoteIpAddress?.ToString() ?? "";
var data = await localStorage.GetAsync<string>(StorageKey);
var state = JsonSerializer.Deserialize<MdlAppState>(data.Value);
if (state != null)
{
if (DateTimeOffset.Now < state.DateExpires)
{
// decide whether to set properties manually or with reflection
// comment to set properties manually
//this.Message = state.Message;
//this.Count = state.Count;
// set properties using Reflaction
var t = typeof(IAppState);
//var props = t.GetProperties();
PropertyInfo[] props = t.GetProperties();
foreach (var prop in props)
{
//if (!Regex.IsMatch(prop.Name, "isInit|SessionId|"))
{
var value = prop.GetValue(state, null);
prop.SetValue(this, value, null);
}
}
}
}
}
catch (Exception ex)
{
// do something
}
isLoaded = true;
DateExpires = DateTimeOffset.Now.AddHours(36);
myIpAddress = remoteIpAddr;
if (!isInit)
{
DateCreated = DateTimeOffset.Now;
SessionId = Guid.NewGuid();
isInit = true;
await SaveAsync();
}
await InvokeAsync(() =>
{
StateHasChanged();
});
} //LoadAsync
}

10
AppState/IAppState.cs Normal file
View File

@ -0,0 +1,10 @@
namespace SummerBestWebForm2.AppState;
public interface IAppState
{
bool isInit { get; set; }
string? myIpAddress { get; set; }
Guid SessionId { get; set; }
DateTimeOffset DateCreated { get; set; }
DateTimeOffset DateExpires { get; set; }
}

10
AppState/MdlAppState.cs Normal file
View File

@ -0,0 +1,10 @@
namespace SummerBestWebForm2.AppState;
public class MdlAppState : IAppState
{
public bool isInit { get; set; }
public string? myIpAddress { get; set; }
public Guid SessionId { get; set; }
public DateTimeOffset DateCreated { get; set; }
public DateTimeOffset DateExpires { get; set; }
}

View File

@ -0,0 +1,15 @@
namespace SummerBestWebForm2;
public class MdlSessionInfo
{
public const string KEY_IPADDRESS = "IPAddress";
public string IPAddress { get; set; } = null!;
public bool isInit { get; set; } = false;
public DateTimeOffset DateCreated { get; set; } = DateTimeOffset.MinValue;
public MdlSessionInfo()
{
// Have to have a blank ctor here for ProtectedSessionStorage
}
}

View File

@ -1,20 +1,92 @@
<!DOCTYPE html>
@inject IHttpContextAccessor httpContextAccessor
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base href="/" />
<link rel="stylesheet" href="bootstrap/bootstrap.min.css" />
<link rel="stylesheet" href="app.css" />
<link rel="stylesheet" href="SummerBestWebForm2.styles.css" />
<link rel="icon" type="image/png" href="favicon.png" />
<HeadOutlet @rendermode="InteractiveServer" />
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base href="/" />
<meta name="description" content="KeyMotive's Summer Best Customer Program Enrollment" />
<meta name="author" content="KeyMotive LLC" />
<meta name="Charset" content="US-ASCII" />
<meta name="Distribution" content="Global" />
<meta name="Rating" content="General" />
<meta name="Robots" content="INDEX,FOLLOW" />
<meta name="Revisit-after" content="14 Days" />
<link rel="preconnect" href="https://fonts.gstatic.com/" crossorigin />
<link href='https://fonts.googleapis.com/css?family=Roboto|Handlee|Candal|Exo 2|Armata|Poppins|Kalam:wght@300&display=swap' rel='stylesheet' type='text/css' />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/css/bootstrap.min.css" integrity="sha512-jnSuA4Ss2PkkikSOLtYs8BlYIeeIK1h99ty4YfvRPAlzr377vr3CXDb7sb7eEEBYjDtcYj+AjBH3FLv5uSJuXg==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css" integrity="sha512-SnH5WK+bZxgPHs44uWIX+LLJAJ9/2PkPKZ5QiAj6Ta86w+fsb2TkcmfRyVX3pBnMFcV7oQPJkl9QevSCWr3W6A==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<link rel="stylesheet" href="app.css" />
<link rel="stylesheet" href="SummerBestWebForm2.styles.css" />
<link rel="icon" type="image/png" sizes="512x512" href="/img/favico-512.png">
<link rel="icon" type="image/png" sizes="256x256" href="/img/favico-256.png">
<link rel="icon" type="image/png" sizes="128x128" href="/img/favico-128.png">
<link rel="icon" type="image/png" sizes="64x64" href="/img/favico-64.png">
<link rel="icon" type="image/png" sizes="32x32" href="/img/favico-32.png">
<style type="text/css">
div.container {
background-color: #fff;
min-height: 100vh;
}
</style>
<HeadOutlet @rendermode="InteractiveServer" />
</head>
<body>
<Routes @rendermode="InteractiveServer" />
<script src="_framework/blazor.web.js"></script>
<Routes @rendermode="InteractiveServer" />
<script src="_framework/blazor.web.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/js/bootstrap.bundle.min.js" integrity="sha512-7Pi/otdlbbCR+LnW+F7PwFcSDJOuUJB3OxtEHbg4vSMvzvJjde4Po1v4BR9Gdc9aXNUNFVUY+SK51wWT8WF0Gg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/js/all.min.js" integrity="sha512-u3fPA7V8qQmhBPNT5quvaXVa1mnnLSXUep5PS1qo5NRzHwG19aHmNJnj1Q8hpA/nBWZtZD4r4AX6YOt5ynLN2g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script>
window.WriteCookie = {
WriteCookie: function (name, value, days) {
var expires;
if (days) {
var date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
expires = "; expires=" + date.toGMTString();
}
else {
expires = "";
}
document.cookie = name + "=" + value + expires + "; path=/";
}
}
window.ReadCookie = {
ReadCookie: function (cname) {
var name = cname + "=";
var decodedCookie = decodeURIComponent(document.cookie);
var ca = decodedCookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) == ' ') {
c = c.substring(1);
}
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length);
}
}
return "";
}
}
</script>
<pre>@errMsg</pre>
@if (appState != null)
{
<pre style="font-size:.8em">
SessionId: @appState.SessionId
DateCreated: @appState.DateCreated
DateExpires: @appState.DateExpires
IPAddress: @appState.myIpAddress
</pre>
}
</body>
</html>
@code {
[CascadingParameter]
public CascadingAppState appState { get; set; }
private string errMsg = string.Empty;
}

View File

@ -8,3 +8,4 @@
@using Microsoft.JSInterop
@using SummerBestWebForm2
@using SummerBestWebForm2.Components
@using SummerBestWebForm2.AppState

View File

@ -1,36 +1,65 @@
using Microsoft.AspNetCore.ResponseCompression;
using SummerBestWebForm2.Components;
using System.IO.Compression;
namespace SummerBestWebForm2
{
public class Program
public class Program
{
public static void Main(string[] args)
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
// Add services to the container.
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
var app = builder.Build();
builder.Services.AddResponseCompression(options =>
{
options.EnableForHttps = true;
options.Providers.Add<BrotliCompressionProvider>();
options.Providers.Add<GzipCompressionProvider>();
});
builder.Services.Configure<BrotliCompressionProviderOptions>(options =>
{
options.Level = CompressionLevel.Fastest;
});
builder.Services.Configure<GzipCompressionProviderOptions>(options =>
{
options.Level = CompressionLevel.SmallestSize;
});
builder.Services.AddDataProtection();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
builder.Services.AddHttpContextAccessor();
builder.Services.AddScoped<HttpContextAccessor>();
app.UseHttpsRedirection();
var app = builder.Build();
app.UseStaticFiles();
app.UseAntiforgery();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();
var webSocketOptions = new WebSocketOptions()
{
KeepAliveInterval = TimeSpan.FromSeconds(15)
};
app.UseWebSockets(webSocketOptions);
app.UseResponseCompression();
app.UseResponseCaching();
app.Run();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAntiforgery();
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();
app.Run();
}
}
}

View File

@ -0,0 +1,12 @@
namespace SummerBestWebForm2.SessionState;
public class MdlSession : Dictionary<string, string>
{
/// <summary>
/// Gets or sets the Session Id value.
/// </summary>
public string SessionId { get; set; } = string.Empty;
public string IPAddress { get; set; } = string.Empty;
public bool IsCheckedOut { get; set; } = false;
public DateTimeOffset dtExpires { get; set; } = DateTimeOffset.Now.AddHours(36);
}

View File

@ -0,0 +1,57 @@
namespace SummerBestWebForm2.SessionState;
// INTERFACE
public interface ISessionIdManager
{
Task<string?> GetSessionIdAsync();
Task<string?> GetIPAddressAsync();
}
// CODE
public class SessionIdManager(IHttpContextAccessor httpContextAccessor) : ISessionIdManager
{
private readonly IHttpContextAccessor HttpContextAccessor = httpContextAccessor;
public Task<string?> GetSessionIdAsync()
{
var httpContext = HttpContextAccessor.HttpContext;
string? result;
if (httpContext != null)
{
if (httpContext.Request.Cookies.ContainsKey("sessionId"))
{
result = httpContext.Request.Cookies["sessionId"];
}
else
{
result = Guid.NewGuid().ToString();
httpContext.Response.Cookies.Append("sessionId", result);
}
}
else
{
throw new InvalidOperationException("No HttpContext available");
}
return Task.FromResult(result);
}
public Task<string?> GetIPAddressAsync()
{
var httpContext = HttpContextAccessor.HttpContext;
string? result;
if (httpContext != null)
{
result = httpContext.Connection.RemoteIpAddress?.ToString();// ?? "Not Set";
}
else
{
throw new InvalidOperationException("No HttpContext available");
}
return Task.FromResult(result);
}
}

View File

@ -0,0 +1,132 @@
using System.Net;
namespace SummerBestWebForm2.SessionState;
// INTERFACE
public interface ISessionManager
{
Task<MdlSession> GetSession();
Task UpdateSession(MdlSession session);
}
// CODE
/// <summary>
/// Dictionary containing per-user session objects, keyed
/// by sessionId.
/// </summary>
public class SessionManager : ISessionManager
{
private Dictionary<Guid, MdlSession> _sessions = new Dictionary<Guid, MdlSession>();
private readonly ISessionIdManager _sessionIdManager;
private object syncLock1 = new();
public SessionManager(ISessionIdManager sessionIdManager)
{
_sessionIdManager = sessionIdManager;
}
public async Task<MdlSession> GetSession()
{
string key = await _sessionIdManager.GetSessionIdAsync() ?? "";
string ipAddress = await _sessionIdManager.GetIPAddressAsync() ?? "";
Guid theSessionId;
if (!Guid.TryParse(key, out theSessionId))
theSessionId = Guid.Empty;//Guid.Parse(key);
MdlSession session;
bool needToCreateNew = false;
if (!_sessions.ContainsKey(theSessionId))
{
needToCreateNew = true;
}
else if (_sessions[theSessionId].dtExpires < DateTimeOffset.Now)
needToCreateNew = true;
if (needToCreateNew)
{
session = new MdlSession() { SessionId = key, IPAddress = ipAddress };
_sessions.Add(theSessionId, new MdlSession());
}
else
session = _sessions[theSessionId];
// ensure session isn't checked out by wasm
//while (session.IsCheckedOut)
// await Task.Delay(5);
var endTime = DateTime.UtcNow.AddSeconds(10);
while (session.IsCheckedOut)
{
if (DateTime.UtcNow > endTime)
throw new TimeoutException();
await Task.Delay(5);
}
return session;
}
public async Task UpdateSession(MdlSession session)
{
if (session != null)
{
string key = await _sessionIdManager.GetSessionIdAsync() ?? "";
string ipAddress = await _sessionIdManager.GetIPAddressAsync() ?? "";
Guid theSessionId;
if (!Guid.TryParse(key, out theSessionId))
theSessionId = Guid.Empty;//Guid.Parse(key);
if (_sessions.ContainsKey(theSessionId))
{
//session = new MdlSession() { SessionId = key, IPAddress = ipAddress };
session.SessionId = key;
session.dtExpires = DateTimeOffset.Now.AddHours(36); // Rolling expiration date
_sessions[theSessionId] = session;
_sessions[theSessionId].IsCheckedOut = false;
}
else
{
var sess = new MdlSession() { SessionId = key, IPAddress = ipAddress };
_sessions[theSessionId] = session;
}
// Remove stale sessions
lock (syncLock1)
{
try
{
var lstRemove = new HashSet<Guid>();
foreach (var kvp in _sessions)
{
if (kvp.Value.dtExpires < DateTimeOffset.Now && !lstRemove.Contains(kvp.Key))
lstRemove.Add(kvp.Key);
}
foreach (var rmv in lstRemove)
_sessions.Remove(rmv);
lstRemove.Clear();
}
catch (Exception ex)
{
// do something
}
}
}
} //UpdateSession
/// <summary>
/// Replace the contents of oldSession with the items
/// in newSession.
/// </summary>
/// <param name="newSession"></param>
/// <param name="oldSession"></param>
private void Replace(MdlSession newSession, MdlSession oldSession)
{
oldSession.Clear();
foreach (var key in newSession.Keys)
oldSession.Add(key, newSession[key]);
}
}

View File

@ -14,7 +14,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("SummerBestWebForm2")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+92bc4657300f745b2cfa9a7b2055bd3fd4591564")]
[assembly: System.Reflection.AssemblyProductAttribute("SummerBestWebForm2")]
[assembly: System.Reflection.AssemblyTitleAttribute("SummerBestWebForm2")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]

View File

@ -1 +1 @@
98a44021c4ce1eb513aa177c5c56797f58eab237be1d5eb9823389d6970b0f70
17e471ad6ef47e2a4bd7241cad398f501e58c3d66500f6f4af29608363c718b3

View File

@ -20,6 +20,10 @@ build_property._RazorSourceGeneratorDebug =
build_property.EffectiveAnalysisLevelStyle = 8.0
build_property.EnableCodeStyleSeverity =
[C:/Repos/SummerBestWebForm2/AppState/CascadingAppState.razor]
build_metadata.AdditionalFiles.TargetPath = QXBwU3RhdGVcQ2FzY2FkaW5nQXBwU3RhdGUucmF6b3I=
build_metadata.AdditionalFiles.CssScope =
[C:/Repos/SummerBestWebForm2/Components/App.razor]
build_metadata.AdditionalFiles.TargetPath = Q29tcG9uZW50c1xBcHAucmF6b3I=
build_metadata.AdditionalFiles.CssScope =

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

BIN
wwwroot/img/RufusHappy.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
wwwroot/img/RufusSad.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

BIN
wwwroot/img/favico-128.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

BIN
wwwroot/img/favico-256.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
wwwroot/img/favico-32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
wwwroot/img/favico-512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
wwwroot/img/favico-64.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB