From c1dd0f91d9747164ff47f6ff40f53c4f039a99ed Mon Sep 17 00:00:00 2001 From: Taylor Southwick Date: Thu, 30 Jun 2022 11:33:15 -0700 Subject: [PATCH 1/4] Separate out JSON session from others so it can be overridden --- .../JsonRemoteAppSessionStateExtensions.cs | 36 ++++++++++++++++++ .../RemoteAppSessionStateExtensions.cs | 19 ++++------ .../JsonSessionSerializerExtensions.cs | 32 ++++++++++++++++ .../SessionSerializerExtensions.cs | 20 ---------- .../Serialization/SessionValues.Shared.cs | 37 ------------------- 5 files changed, 76 insertions(+), 68 deletions(-) create mode 100644 src/Microsoft.AspNetCore.SystemWebAdapters.SessionState/RemoteSession/Framework/JsonRemoteAppSessionStateExtensions.cs create mode 100644 src/Microsoft.AspNetCore.SystemWebAdapters.SessionState/Serialization/JsonSessionSerializerExtensions.cs delete mode 100644 src/Microsoft.AspNetCore.SystemWebAdapters.SessionState/Serialization/SessionValues.Shared.cs diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.SessionState/RemoteSession/Framework/JsonRemoteAppSessionStateExtensions.cs b/src/Microsoft.AspNetCore.SystemWebAdapters.SessionState/RemoteSession/Framework/JsonRemoteAppSessionStateExtensions.cs new file mode 100644 index 0000000000..c5b8ce5e32 --- /dev/null +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.SessionState/RemoteSession/Framework/JsonRemoteAppSessionStateExtensions.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using Microsoft.AspNetCore.SystemWebAdapters.SessionState.RemoteSession; +using Microsoft.AspNetCore.SystemWebAdapters.SessionState.Serialization; + +namespace Microsoft.AspNetCore.SystemWebAdapters; + +public static class JsonRemoteAppSessionStateExtensions +{ + public static ISystemWebAdapterBuilder AddJsonRemoteAppSession(this ISystemWebAdapterBuilder builder, Action configureRemote, Action configureSerializer) + { + if (builder is null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (configureRemote is null) + { + throw new ArgumentNullException(nameof(configureRemote)); + } + + if (configureSerializer is null) + { + throw new ArgumentNullException(nameof(configureSerializer)); + } + + var serializerOptions = new JsonSessionSerializerOptions(); + configureSerializer(serializerOptions); + + var keySerializer = new JsonSessionKeySerializer(serializerOptions); + + return builder.AddRemoteAppSession(configureRemote, keySerializer); + } +} diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.SessionState/RemoteSession/Framework/RemoteAppSessionStateExtensions.cs b/src/Microsoft.AspNetCore.SystemWebAdapters.SessionState/RemoteSession/Framework/RemoteAppSessionStateExtensions.cs index 0d3195d7bd..45fc6cae33 100644 --- a/src/Microsoft.AspNetCore.SystemWebAdapters.SessionState/RemoteSession/Framework/RemoteAppSessionStateExtensions.cs +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.SessionState/RemoteSession/Framework/RemoteAppSessionStateExtensions.cs @@ -9,7 +9,7 @@ namespace Microsoft.AspNetCore.SystemWebAdapters; public static class RemoteAppSessionStateExtensions { - public static ISystemWebAdapterBuilder AddRemoteAppSession(this ISystemWebAdapterBuilder builder, Action configureRemote, Action configureSerializer) + public static ISystemWebAdapterBuilder AddRemoteAppSession(this ISystemWebAdapterBuilder builder, Action configureRemote, ISessionKeySerializer keySerializer) { if (builder is null) { @@ -21,22 +21,19 @@ public static ISystemWebAdapterBuilder AddRemoteAppSession(this ISystemWebAdapte throw new ArgumentNullException(nameof(configureRemote)); } - if (configureSerializer is null) + if (keySerializer is null) { - throw new ArgumentNullException(nameof(configureSerializer)); + throw new ArgumentNullException(nameof(keySerializer)); } - var options = new RemoteAppSessionStateOptions(); - configureRemote(options); + var remoteOptions = new RemoteAppSessionStateOptions(); + configureRemote(remoteOptions); // We don't want to throw by default on the .NET Framework side as then the error won't be easily visible in the ASP.NET Core app - var serializerOptions = new JsonSessionSerializerOptions(); - configureSerializer(serializerOptions); + var serializerOptions = new SessionSerializerOptions { ThrowOnUnknownSessionKey = false }; + var serializer = new BinarySessionSerializer(keySerializer, serializerOptions); - var keySerializer = new JsonSessionKeySerializer(serializerOptions); - var serializer = new BinarySessionSerializer(keySerializer, new SessionSerializerOptions { ThrowOnUnknownSessionKey = false }); - - builder.Modules.Add(new RemoteSessionModule(options, new InMemoryLockedSessions(serializer), serializer)); + builder.Modules.Add(new RemoteSessionModule(remoteOptions, new InMemoryLockedSessions(serializer), serializer)); return builder; } diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.SessionState/Serialization/JsonSessionSerializerExtensions.cs b/src/Microsoft.AspNetCore.SystemWebAdapters.SessionState/Serialization/JsonSessionSerializerExtensions.cs new file mode 100644 index 0000000000..f7c723a820 --- /dev/null +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.SessionState/Serialization/JsonSessionSerializerExtensions.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using Microsoft.AspNetCore.SystemWebAdapters.SessionState.Serialization; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; + +namespace Microsoft.AspNetCore.SystemWebAdapters; + +public static class JsonSessionSerializerExtensions +{ + public static ISystemWebAdapterBuilder AddJsonSessionSerializer(this ISystemWebAdapterBuilder builder, Action? configure = null) + { + if (builder is null) + { + throw new ArgumentNullException(nameof(builder)); + } + + builder.AddSessionSerializer(); + builder.Services.TryAddSingleton(); + + var options = builder.Services.AddOptions(); + + if (configure is not null) + { + options.Configure(configure); + } + + return builder; + } +} diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.SessionState/Serialization/SessionSerializerExtensions.cs b/src/Microsoft.AspNetCore.SystemWebAdapters.SessionState/Serialization/SessionSerializerExtensions.cs index cae1c28324..7cd8638fb8 100644 --- a/src/Microsoft.AspNetCore.SystemWebAdapters.SessionState/Serialization/SessionSerializerExtensions.cs +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.SessionState/Serialization/SessionSerializerExtensions.cs @@ -28,24 +28,4 @@ public static ISystemWebAdapterBuilder AddSessionSerializer(this ISystemWebAdapt return builder; } - - public static ISystemWebAdapterBuilder AddJsonSessionSerializer(this ISystemWebAdapterBuilder builder, Action? configure = null) - { - if (builder is null) - { - throw new ArgumentNullException(nameof(builder)); - } - - builder.AddSessionSerializer(); - builder.Services.TryAddSingleton(); - - var options = builder.Services.AddOptions(); - - if (configure is not null) - { - options.Configure(configure); - } - - return builder; - } } diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.SessionState/Serialization/SessionValues.Shared.cs b/src/Microsoft.AspNetCore.SystemWebAdapters.SessionState/Serialization/SessionValues.Shared.cs deleted file mode 100644 index f04373103f..0000000000 --- a/src/Microsoft.AspNetCore.SystemWebAdapters.SessionState/Serialization/SessionValues.Shared.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Text.Json.Serialization; - -namespace Microsoft.AspNetCore.SystemWebAdapters.SessionState.Serialization; - -internal class SessionValues : NameObjectCollectionBase -{ - [JsonIgnore] - public List? UnknownKeys { get; set; } - - public void Add(string key, object? value) => BaseAdd(key, value); - - public void Clear() => BaseClear(); - - public void Remove(string key) => BaseRemove(key); - - public new IEnumerable Keys - { - get - { - foreach (var key in base.Keys) - { - yield return (string)key!; - } - } - } - - public object? this[string key] - { - get => BaseGet(key); - set => BaseSet(key, value); - } -} From 3a5ad9f093c8d3c2572265f858ce7335a3a851ef Mon Sep 17 00:00:00 2001 From: Taylor Southwick Date: Thu, 30 Jun 2022 12:19:38 -0700 Subject: [PATCH 2/4] fix test --- samples/MvcApp/Global.asax.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/MvcApp/Global.asax.cs b/samples/MvcApp/Global.asax.cs index 638ab4e681..3a3cea9095 100644 --- a/samples/MvcApp/Global.asax.cs +++ b/samples/MvcApp/Global.asax.cs @@ -18,7 +18,7 @@ protected void Application_Start() Application.AddSystemWebAdapters() .AddProxySupport(options => options.UseForwardedHeaders = true) - .AddRemoteAppSession( + .AddJsonRemoteAppSession( ConfigureRemoteServiceOptions, options => ClassLibrary.RemoteServiceUtils.RegisterSessionKeys(options.KnownKeys)) .AddRemoteAppAuthentication(o => ConfigureRemoteServiceOptions(o.RemoteServiceOptions)); From 6671b793c3538728b11a55ddf1b44fda2c5623c9 Mon Sep 17 00:00:00 2001 From: Taylor Southwick Date: Thu, 30 Jun 2022 12:28:21 -0700 Subject: [PATCH 3/4] update docs --- docs/session-state/remote-session.md | 14 +++++++++++++- docs/session-state/session.md | 5 ++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/docs/session-state/remote-session.md b/docs/session-state/remote-session.md index a4a0b11e25..4b3b8b1fc5 100644 --- a/docs/session-state/remote-session.md +++ b/docs/session-state/remote-session.md @@ -2,6 +2,18 @@ Remote app session state will enable communication between the ASP.NET Core and ASP.NET app and to retrieve the session state. This is enabled by exposing an endpoint on the ASP.NET app that can be queried to retrieve and set the session state. +# HttpSessionState serialization + +The `System.Web.SessionState.HttpSessionState` object must be serialized for remote app session state to be enabled. This is accomplished through implementation of the type `Microsoft.AspNetCore.SysteWebAdapters.SessionState.Serialization.ISessionSerializer`, of which a default binary writer implementation is provided. This is added by the following code: + +```csharp +builder.Services.AddSystemWebAdapters() + .AddSessionSerializer(options => + { + // Customize session serialization here + }); +``` + ## Configuration In order to configure it, both the framework and core app must set an API key as well as register known app settings types. These properties are: @@ -52,7 +64,7 @@ The framework equivalent would look like the following change in `Global.asax.cs ```csharp Application.AddSystemWebAdapters() - .AddRemoteAppSession( + .AddJsonRemoteAppSession( // Provide a strong API key that will be used to authenticate the request on the remote app for querying the session options => options.ApiKey = "strong-api-key", options => diff --git a/docs/session-state/session.md b/docs/session-state/session.md index ff89063549..79f6120999 100644 --- a/docs/session-state/session.md +++ b/docs/session-state/session.md @@ -11,11 +11,10 @@ The adapter infrastructure exposes two interfaces that can be used to implement - `Microsoft.AspNetCore.SystemWebAdapters.ISessionState`: This describes the state of a session object. It is used as the backing of the `System.Web.SessionState.HttpSessionState` type. ## Serialization -Since the adapters provide the ability to work with strongly-typed session state, we must be able to serialize and deserialize types. This is accomplished through implementation of the type `Microsoft.AspnetCore.SysteWebAdapters.SessionState.Serialization.ISessionSerializer`, of which a JSON implementation is provided. +Since the adapters provide the ability to work with strongly-typed session state, we must be able to serialize and deserialize types. This is customized through the `Microsoft.AspNetCore.SystemWebAdapters.SessionState.Serialization.ISessionKeySerializer`. -Serialization and deserialization of session keys requires additional information which is configured via the `JsonSessionSerializerOptions`: +A default JSON implementation is provided that is configured via the `JsonSessionSerializerOptions`: -- `ThrowOnUnknownSessionKey` - Gets or sets if an unknown key should cause serialization to fail or if it will log a warning and ignore it. - `RegisterKey(string)` - Registers a session key to a known type. This is required in order to serialize/deserialize the session state correctly. If a key is found that there is no registration for, an error will be thrown and session will not be available. To use the default JSON backed implementation, add a package reference to [Microsoft.AspNetCore.SystemWebAdapters.SessionState](https://www.nuget.org/packages/Microsoft.AspNetCore.SystemWebAdapters.SessionState) and add the following to the startup: From 5c4ef90835a62a0764ac05f213f938962f13c876 Mon Sep 17 00:00:00 2001 From: Taylor Southwick Date: Thu, 30 Jun 2022 12:48:01 -0700 Subject: [PATCH 4/4] Add overload for ISessionSerializer --- .../RemoteAppSessionStateExtensions.cs | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.SessionState/RemoteSession/Framework/RemoteAppSessionStateExtensions.cs b/src/Microsoft.AspNetCore.SystemWebAdapters.SessionState/RemoteSession/Framework/RemoteAppSessionStateExtensions.cs index 45fc6cae33..ed5a43a4de 100644 --- a/src/Microsoft.AspNetCore.SystemWebAdapters.SessionState/RemoteSession/Framework/RemoteAppSessionStateExtensions.cs +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.SessionState/RemoteSession/Framework/RemoteAppSessionStateExtensions.cs @@ -26,13 +26,33 @@ public static ISystemWebAdapterBuilder AddRemoteAppSession(this ISystemWebAdapte throw new ArgumentNullException(nameof(keySerializer)); } - var remoteOptions = new RemoteAppSessionStateOptions(); - configureRemote(remoteOptions); - // We don't want to throw by default on the .NET Framework side as then the error won't be easily visible in the ASP.NET Core app var serializerOptions = new SessionSerializerOptions { ThrowOnUnknownSessionKey = false }; var serializer = new BinarySessionSerializer(keySerializer, serializerOptions); + return builder.AddRemoteAppSession(configureRemote, serializer); + } + + public static ISystemWebAdapterBuilder AddRemoteAppSession(this ISystemWebAdapterBuilder builder, Action configureRemote, ISessionSerializer serializer) + { + if (builder is null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (configureRemote is null) + { + throw new ArgumentNullException(nameof(configureRemote)); + } + + if (serializer is null) + { + throw new ArgumentNullException(nameof(serializer)); + } + + var remoteOptions = new RemoteAppSessionStateOptions(); + configureRemote(remoteOptions); + builder.Modules.Add(new RemoteSessionModule(remoteOptions, new InMemoryLockedSessions(serializer), serializer)); return builder;