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: 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)); 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..ed5a43a4de 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,39 @@ 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); - // 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); + + 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 keySerializer = new JsonSessionKeySerializer(serializerOptions); - var serializer = new BinarySessionSerializer(keySerializer, new SessionSerializerOptions { ThrowOnUnknownSessionKey = false }); + var remoteOptions = new RemoteAppSessionStateOptions(); + configureRemote(remoteOptions); - 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); - } -}