Is there a session start equivalent in .Net Core M

2020-07-17 02:15发布

问题:

In MVC 5 you could assign a value to session in global.asx when the session started. Is there a way you can do this in .Net Core MVC? I have session configured but in the middleware it seems to get called on every request.

回答1:

nercan's solution will work, but I think I found a solution that requires less code and may have other advantages.

First, wrap DistributedSessionStore like this:

using System;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Session;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;

public interface IStartSession
{
    void StartSession(ISession session);
}

public class DistributedSessionStoreWithStart : ISessionStore
{
    DistributedSessionStore innerStore;
    IStartSession startSession;
    public DistributedSessionStoreWithStart(IDistributedCache cache, 
        ILoggerFactory loggerFactory, IStartSession startSession)
    {
        innerStore = new DistributedSessionStore(cache, loggerFactory);
        this.startSession = startSession;
    }

    public ISession Create(string sessionKey, TimeSpan idleTimeout, 
        TimeSpan ioTimeout, Func<bool> tryEstablishSession, 
        bool isNewSessionKey)
    {
        ISession session = innerStore.Create(sessionKey, idleTimeout, ioTimeout,
             tryEstablishSession, isNewSessionKey);
        if (isNewSessionKey)
        {
            startSession.StartSession(session);
        }
        return session;
    }
}

Then register this new class in Startup.cs:

class InitSession : IStartSession
{
    public void StartSession(ISession session)
    {
        session.SetString("Hello", "World");
    }
}

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        ...
        services.AddSingleton<IStartSession, InitSession>();
        services.AddSingleton<ISessionStore, DistributedSessionStoreWithStart>();
        services.AddSession();
        ...
    }

Full code is here: https://github.com/SurferJeffAtGoogle/scratch/tree/master/StartSession/MVC



回答2:

I use it in a live project. It works correctly. if you want to keep it when the application stops. You should use DistributedCache. For example, I'm using DistributedRedisCache.

Add to startup this code;

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSession(options =>
        {
            options.IdleTimeout = TimeSpan.FromMinutes(60);
            options.Cookie.HttpOnly = true;
        });
// for redis distributed cache

//services.AddDistributedRedisCache(options =>
            //    {
                 //   options.InstanceName = $"{Configuration["DistributedRedisCacheInstance"]}";
                 //   options.Configuration = $"{Configuration["DistributedRedisCacheHost"]}";
              //  });
    }
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, IHttpContextAccessor acc)
    {
        app.UseSession();

    }

And add new session extension;

using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
using System.Text;

namespace SampleApp
{
    public static class SessionExtensions
    {
        public static void SetObjectAsJson<T>(this ISession session, string key, T value)
        {
            session.Set(key, Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(value)));
        }

        public static T GetObjectFromJson<T>(this ISession session, string key)
        {
            session.TryGetValue(key, out byte[] dataByte);
            string data = dataByte != null ? Encoding.UTF8.GetString(dataByte) : null;

            return data == null ? default(T) : JsonConvert.DeserializeObject<T>(data);
        }
    }
}

And use get or set same this;

var sessionItem = httpContext.Session.GetObjectFromJson<string>("sessionItem");
//or


ContextProviderExtension.HttpContextAccessor.HttpContext.Session.SetObjectAsJson("sessionItem", sessionItem);

you need this extension;

using Microsoft.AspNetCore.Http;
using System;

namespace SampleApp
{
    public static class ContextProviderExtension
    {
        static IHttpContextAccessor httpContextAccessor = null;
        public static IHttpContextAccessor HttpContextAccessor
        {
            get { return httpContextAccessor; }
            set
            {
                if (httpContextAccessor != null)
                {
                    throw new Exception("");
                }
                httpContextAccessor = value;
            }
        }
    }
}

I suppose it will work.

 using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;


namespace SampleApp
{
    public class SessionMiddleware
    {
        private readonly RequestDelegate _next;

        public SessionMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task Invoke(HttpContext httpContext)
        {
            var sessionItem = httpContext.Session.GetObjectFromJson<string>("test");
            if (sessionItem == null)
                httpContext.Session.SetObjectAsJson<string>("test", httpContext.Session.Id);//httpContext.Session.Id or set a value
            await _next.Invoke(httpContext);
        }
    }

    public static class SessionMiddlewareExtensions
    {
        public static IApplicationBuilder UseSessionMiddleware(this IApplicationBuilder app)
        {
            return app.UseMiddleware<SessionMiddleware>();
        }
    }
}

and add startup.cs Configure method after app.UseSession();

app.UseSessionMiddleware();