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(("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"));
});
3202User
  • 371
  • 4
  • 19
  • 1
    Can you share the code where you inject the service you call? What is the scope of IHttpContextAccessor (I assume you use DI)? – A_kat Feb 21 '20 at 14:40
  • @A_kat about your first question did mean Front-end code? about to IHttpContextAccessor, I used Microsoft.AspNetCore.Http – 3202User Feb 21 '20 at 16:18
  • `using Microsoft.AspNetCore.Http; namespace Microsoft.AspNetCore.Http { public interface IHttpContextAccessor { HttpContext HttpContext { get; set; } } }` – 3202User Feb 21 '20 at 16:24
  • Can you provide your application initialization code? You need to configure the session in the Startup for it to work. – AlesD Mar 15 '20 at 22:49
  • @AlesD I've added Startup class code – 3202User Mar 17 '20 at 05:19

2 Answers2

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

} 
AimusSage
  • 551
  • 3
  • 5
  • I can't use services.AddTransient it doesn't reconize ! – 3202User Mar 18 '20 at 10:13
  • 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. – AimusSage Mar 18 '20 at 10:36
  • @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? – 3202User Mar 18 '20 at 10:56
  • 1
    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](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/app-state?view=aspnetcore-3.1#signalr-and-session-state) I'll update the answer. – AimusSage Mar 18 '20 at 14:04
  • @AimusSage 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? – burg.developer Apr 17 '20 at 10:51
  • @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. – AimusSage Apr 18 '20 at 17:07
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.

AlesD
  • 9,260
  • 16
  • 30
  • I can't use app.UseHttpContextItemsMiddleware(); it doesn't reconize it ! – 3202User Mar 18 '20 at 10:13
  • 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. – AlesD Mar 18 '20 at 12:52