Set and Get session variable in ASP.NET Boilerplate .NET Core v3
Asked Answered
F

2

6

I'm trying to set variable value (selected option from a select component in angular) in some component to use it in another component and I don't want to use window.sessionStorage() by javascript I want to use session storage in the .NET core but the problem is I could set the variable value but when I want to get it, it didn't return the value.

Below is my Application Service Code

 public class MainProjectAppService : AsyncCrudAppService<MainProject, MainProjectDto,int,PagedAndSortedResultRequestDto,MainProjectDto>
    {



         private readonly IHttpContextAccessor _httpContextAccessor;


        public MainProjectAppService(IRepository<MainProject, int> repository, IHttpContextAccessor httpContextAccessor) : base(repository)
        {

            _httpContextAccessor = httpContextAccessor;
        }

        public void setVaraibleValurinSesseion(int ID)
        {
            _httpContextAccessor.HttpContext.Session.SetInt32("GID", ID);
        }

        public int getVaraibleValurFromSesseion()
        {
            return (int)_httpContextAccessor.HttpContext.Session.GetInt32("GID");

        }

And here is my Updated Startup.cs class code :


using System;
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Castle.Facilities.Logging;
using Abp.AspNetCore;
using Abp.AspNetCore.Mvc.Antiforgery;
using Abp.Castle.Logging.Log4Net;
using Abp.Extensions;
using TTT.Configuration;
using TTT.Identity;
using Abp.AspNetCore.SignalR.Hubs;
using Abp.Dependency;
using Abp.Json;
using Microsoft.OpenApi.Models;
using Newtonsoft.Json.Serialization;
using Microsoft.AspNetCore.Http;

namespace TTT.Web.Host.Startup
{
    public class Startup
    {
        private const string _defaultCorsPolicyName = "localhost";

        private readonly IConfigurationRoot _appConfiguration;

        public Startup(IWebHostEnvironment env)
        {
            _appConfiguration = env.GetAppConfiguration();
        }

        public IServiceProvider ConfigureServices(IServiceCollection services)
        {
            services.AddDistributedMemoryCache(); //added this

            services.AddSession(options =>
            {
                options.IdleTimeout = TimeSpan.FromSeconds(10);
                options.Cookie.HttpOnly = true;
                options.Cookie.IsEssential = true;
            });//added this 
            //MVC
            services.AddControllersWithViews(
                options =>
                {
                    options.Filters.Add(new AbpAutoValidateAntiforgeryTokenAttribute());
                }
            ).AddNewtonsoftJson(options =>
            {
                options.SerializerSettings.ContractResolver = new AbpMvcContractResolver(IocManager.Instance)
                {
                    NamingStrategy = new CamelCaseNamingStrategy()
                };
            });

                 services.AddHttpContextAccessor(); // added this but no need as //I think

            services.AddMvc().AddSessionStateTempDataProvider();//added this
            services.AddSession();// added this 

            IdentityRegistrar.Register(services);
            AuthConfigurer.Configure(services, _appConfiguration);

            services.AddSignalR();

            // Configure CORS for angular2 UI
            services.AddCors(
                options => options.AddPolicy(
                    _defaultCorsPolicyName,
                    builder => builder
                        .WithOrigins(
                            // App:CorsOrigins in appsettings.json can contain more than one address separated by comma.
                            _appConfiguration["App:CorsOrigins"]
                                .Split(",", StringSplitOptions.RemoveEmptyEntries)
                                .Select(o => o.RemovePostFix("/"))
                                .ToArray()
                        )
                        .AllowAnyHeader()
                        .AllowAnyMethod()
                        .AllowCredentials()
                )
            );

            // Swagger - Enable this line and the related lines in Configure method to enable swagger UI
            services.AddSwaggerGen(options =>
            {
                options.SwaggerDoc("v1", new OpenApiInfo() { Title = "TTT API", Version = "v1" });
                options.DocInclusionPredicate((docName, description) => true);

                // Define the BearerAuth scheme that's in use
                options.AddSecurityDefinition("bearerAuth", new OpenApiSecurityScheme()
                {
                    Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"",
                    Name = "Authorization",
                    In = ParameterLocation.Header,
                    Type = SecuritySchemeType.ApiKey
                });
            });

            // Configure Abp and Dependency Injection
            return services.AddAbp<TTTWebHostModule>(
                // Configure Log4Net logging
                options => options.IocManager.IocContainer.AddFacility<LoggingFacility>(
                    f => f.UseAbpLog4Net().WithConfig("log4net.config")
                )
            );
        }

        public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
        {

            app.UseAbp(options => { options.UseAbpRequestLocalization = false; }); // Initializes ABP framework.

            app.UseCors(_defaultCorsPolicyName); // Enable CORS!

            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthentication();

            app.UseAbpRequestLocalization();
            //app.UseHttpContextItemsMiddleware();
             app.UseSession(); // <================= I've added this without it //will not work 
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapHub<AbpCommonHub>("/signalr");
                endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
                endpoints.MapControllerRoute("defaultWithArea", "{area}/{controller=Home}/{action=Index}/{id?}");
            });

            // Enable middleware to serve generated Swagger as a JSON endpoint
            app.UseSwagger();
            // Enable middleware to serve swagger-ui assets (HTML, JS, CSS etc.)
            app.UseSwaggerUI(options =>
            {
                options.SwaggerEndpoint(_appConfiguration["App:ServerRootAddress"].EnsureEndsWith('/') + "swagger/v1/swagger.json", "TTT API V1");
                options.IndexStream = () => Assembly.GetExecutingAssembly()
                    .GetManifestResourceStream("TTT.Web.Host.wwwroot.swagger.ui.index.html");
            }); // URL: /swagger
        }
    }
}

Update 3:


Now after added some configuration in Startup.cs class it works by Swagger but I wanna get the value by angular front it as following it returns

System.InvalidOperationException: 'Nullable object must have a value.'

angular set code :

   this.http.post('http://localhost:21021/api/services/app/MainProject/setVaraibleValurinSesseion?ID=555',{ID:555}).subscribe(data=>{
  alert('saved');
});

angular get code :

  this.http.get('http://localhost:21021/api/services/app/MainProject/getVaraibleValurFromSesseion').subscribe(data=>{
  console.log("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$");
      console.log(data);
      console.log(("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"));
});
Fennec answered 21/2, 2020 at 12:21 Comment(5)
Can you share the code where you inject the service you call? What is the scope of IHttpContextAccessor (I assume you use DI)?Antagonist
@Antagonist about your first question did mean Front-end code? about to IHttpContextAccessor, I used Microsoft.AspNetCore.HttpFennec
using Microsoft.AspNetCore.Http; namespace Microsoft.AspNetCore.Http { public interface IHttpContextAccessor { HttpContext HttpContext { get; set; } } }Fennec
Can you provide your application initialization code? You need to configure the session in the Startup for it to work.Garibald
@AlesD I've added Startup class codeFennec
P
1

Use SignalR instead

What you can do instead of using the httpcontext session is use SignalR to store state for a connection:

Microsoft says this on the matter:

SignalR and session state SignalR apps should not use session state to store information. SignalR apps can store per connection state in Context.Items in the hub.

How to do this is described here: SignalR Docs

Basically, what you do is create a hub that provides public methods for your frontend to call and add items to the context. The hub could look something like this.

public class MyAwesomeHub : Hub
    {
        public void setVaraibleValurinSesseion(int ID)
        {
            Context.Items.Add("GID", ID);
        }

        public int getVaraibleValurFromSesseion(string key)
        {
            var value = Context.Items[key];
            return (int)value;
        }
    }

You'll probably need to add some more checks to make sure you set and get the right types of value and so on, the above example is very basic but it should get the job done.

In your startup, you'll need to add the following:

app.UseEndpoints(endpoints =>
{
    endpoints.MapHub<MyAwesomeHub>("/myAwesomeHub");
});

That is the minimum to get it to work, the mentioned documentation by microsoft goes into more detail. You can also check this Microsoft Sample for more details on how to set up the hub in your code.

Original answer

You need to add HttpContextAccessor to your startup as described in the docs:

For other framework and custom components that require access to HttpContext, the recommended approach is to register a dependency using the built-in dependency injection container. The dependency injection container supplies the IHttpContextAccessor to any classes that declare it as a dependency in their constructors:

public void ConfigureServices(IServiceCollection services) {
     services.AddHttpContextAccessor(); // add this one to what is already in your startup.cs

} 
Pogy answered 17/3, 2020 at 10:21 Comment(6)
I can't use services.AddTransient it doesn't reconize <IUserRepository, UserRepository> !Fennec
That is part of the docs example, I'll edit the answer to be more clear. Having said that, it might still not work, as per the answer provided by AlesD.Pogy
@AmiusSage But the problem it works by Swagger but when I wanna use it by front-end it doesn't, does that relate to SignalR? could you please check my updates?Fennec
Things are starting to be a bit messy now in your startup. I see you call AddSessions two times in your startup. Once with options and later without setting options. The idle timeout is also set to 10 seconds. That can cause it to clear the session between front-end calls.However, best bet is to use a hub for SignalR instead to store you info. This is what microsoft recommends as an alternative docs I'll update the answer.Pogy
@Pogy have got a similar problem, I see you updated your answer by using SingR but in my scenario, I don't use MVC I use DDD, does your solution work with DDD?Chock
@burg.developer. I can't answer that without knowing more. I recommend you try and if you can't get it to work put the question on SO.Pogy
C
1

After reviewing your code I see you don't initialize the session services in the startup. To initialize the Session you need to provide a distributed cache service and setup the session services. Here is just the sample configuration bits you need to add in the startup from the official documentation.

public void ConfigureServices(IServiceCollection services)
{
   services.AddDistributedMemoryCache();

   services.AddSession(options =>
   {
      options.IdleTimeout = TimeSpan.FromSeconds(10);
      options.Cookie.HttpOnly = true;
      options.Cookie.IsEssential = true;
   });
   // other needed initializations
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
   app.UseSession();
   // other configurations
}

For more detailed samples check the Microsoft samples in GitHub.

Even with this it will might not work for you because you use SignalR. The documentation states the following:

Session isn't supported in SignalR apps because a SignalR Hub may execute independent of an HTTP context. For example, this can occur when a long polling request is held open by a hub beyond the lifetime of the request's HTTP context.

Caleb answered 17/3, 2020 at 21:30 Comment(2)
I can't use app.UseHttpContextItemsMiddleware(); it doesn't reconize it !Fennec
Yes the documentation sample uses some custom Middleware. Try wit app.UseSession i have updated the answer and added also link to samples provided by Microsoft in GitHub.Garibald

© 2022 - 2024 — McMap. All rights reserved.