Spring-session clears attributes from session after pageContext.include - spring-session

I think I have come across a bug in spring-session but I just want to ask here if it really is a bug. Before I forget
https://github.com/paranoiabla/spring-session-issue.git
here's a github repository that reproduces the problem. Basically I have a 2 controllers and 2 jsps, so the flow goes like this:
User opens http://localhost:8080/ and the flow goes through HomepageController, which puts 1 attribute in the spring-session and returns the homepage.jsp which renders the session id and the number of attributes (1)
The homepage.jsp has this line inside it:
${pageContext.include("/include")}
which calls the IncludeController to be invoked.
The IncludeController finds the session from the session repository and LOGs the number of attributes (now absolutely weird they are logged as 0) and returns the include.jsp which renders both the session id and the number of session attributes (0).
The session id in both jsps is the same, but somehow after the pageContext.include call the attributes were reset to an empty map!!!
Can someone please confirm if this is a bug.
Thank you.

Problem
The problem is that when using MapSessionRepository the SessionRepositoryFilter will automatically sync the HttpSession to the Spring Session which overrides explicit use of the APIs. Specifically the following is happening:
SessionRepositoryFilter is obtaining the current Spring Session. It caches it in the HttpServletRequest to ensure that every invocation of HttpServletRequest.getSession() does not make a database call. This cached version of the Spring Session has no attributes associated with it.
The HomepageController obtains its own copy of Spring Session, modifies it, and then saves it.
The JSP flushes the response which commits the HttpServletResponse. This means we must write out the session cookie just prior to the flush being set. We also need to ensure that the session is persisted at this point because immediately afterwards the client may have access to the session id and be able to make another request. This means that the Spring Session from #1 is saved with no attributes which overrides the session saved in #2.
The IncludeController obtains the Spring Session that was saved from #3 (which has no attributes)
Solution
There are two options I see to solving this.
Use HttpSession APIs
So how would I solve this. The easiest approach is to stop using the Spring Session APIs directly. This is preferred anyways since we do not want to tie ourselves to the Spring Session APIs if possible. For example, instead of using the following:
#Controller
public class HomepageController {
#Resource(name = "sessionRepository")
private SessionRepository<ExpiringSession> sessionRepository;
#Resource(name = "sessionStrategy")
private HttpSessionStrategy sessionStrategy;
#RequestMapping(value = "/", method = RequestMethod.GET)
public String home(final Model model) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
final String sessionIds = sessionStrategy.getRequestedSessionId(request);
if (sessionIds != null) {
final ExpiringSession session = sessionRepository.getSession(sessionIds);
if (session != null) {
session.setAttribute("attr", "value");
sessionRepository.save(session);
model.addAttribute("session", session);
}
}
return "homepage";
}
}
#Controller
public class IncludeController {
private final static Logger LOG = LogManager.getLogger(IncludeController.class);
#Resource(name = "sessionRepository")
private SessionRepository<ExpiringSession> sessionRepository;
#Resource(name = "sessionStrategy")
private HttpSessionStrategy sessionStrategy;
#RequestMapping(value = "/include", method = RequestMethod.GET)
public String home(final Model model) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
final String sessionIds = sessionStrategy.getRequestedSessionId(request);
if (sessionIds != null) {
final ExpiringSession session = sessionRepository.getSession(sessionIds);
if (session != null) {
LOG.error(session.getAttributeNames().size());
model.addAttribute("session", session);
}
}
return "include";
}
}
You can simplify it using the following:
#Controller
public class HomepageController {
#RequestMapping(value = "/", method = RequestMethod.GET)
public String home(HttpServletRequest request, Model model) {
String sessionIds = request.getRequestedSessionId();
if (sessionIds != null) {
final HttpSession session = request.getSession(false);
if (session != null) {
session.setAttribute("attr", "value");
model.addAttribute("session", session);
}
}
return "homepage";
}
}
#Controller
public class IncludeController {
#RequestMapping(value = "/include", method = RequestMethod.GET)
public String home(HttpServletRequest request, final Model model) {
final String sessionIds = request.getRequestedSessionId();
if (sessionIds != null) {
final HttpSession session = request.getSession(false);
if (session != null) {
model.addAttribute("session", session);
}
}
return "include";
}
}
Use RedisOperationsSessionRepository
Of course this may be problematic in the event that we cannot use the HttpSession API directly. To handle this, you need to use a different implementation of SessionRepository. For example, another fix is to use the RedisOperationsSessionRepository. This works because it is smart enough to only update attributes that have been changed.
This means in step #3 from above, the Redis implementation will only update the last accessed time since no other attributes were updated. When the IncludeController requests the Spring Session it will still see the attribute saved in HomepageController.
So why doesn't MapSessionRepository do this? Because MapSessionRepository is based on a Map which is an all or nothing thing. When the value is placed in the map it is a single put (we cannot break that up into multiple operations).

Related

Configure/register depdency injection scoped service from within the scope

I have a stateless service in Azure Service Fabric, and I'm using Microsoft.Extensions.DependencyInjection, although the same issue exists for any other DI frameworks. In my Program.cs, I create a ServiceCollection, add all (but one) of my registrations, create the service provider, and pass it to my service's constructor. Any service method with external entry will create a new service scope and call the main business logic class. The issue is that one of the classes I want to have scoped lifetime needs a value that is an input parameter on the request itself. Here's a code snippet of what I would like to achieve.
internal sealed class MyService : StatelessService, IMyService
{
private IServiceProvider _serviceProvider;
private IServiceScopeFactory _scopeFactory;
public MyService(StatelessServiceContext context, IServiceProvider serviceProvider)
: base(context)
{
_serviceProvider = serviceProvider;
_scopeFactory = _serviceProvider.GetRequiredService<IServiceScopeFactory>();
}
public async Task<MyResponse> ProcessAsync(MyRequest request, string correlationId, CancellationToken cancellationToken)
{
using (var scope = _scopeFactory.CreateScope())
{
var requestContext = new RequestContext(correlationId);
//IServiceCollection serviceCollection = ??;
//serviceCollection.AddScoped<RequestContext>(di => requestContext);
var businessLogic = scope.ServiceProvider.GetRequiredService<BusinessLogic>();
return await businessLogic.ProcessAsync(request, cancellationToken);
}
}
}
The cancellation token is already passed around everywhere, including to classes that don't use it directly, just so it can be passed to dependencies that do use it, and I want to avoid doing the same with the request context.
The same issue exists in my MVC APIs. I can create middle-ware which will extract the correlation id from the HTTP headers, so the API controller doesn't need to deal with it like my service fabric service does. One way I can make it work is by giving RequestContext a default constructor, and have a mutable correlation id. However, it's absolutely critical that the correlation id doesn't get changed during a request, so I'd really like the safety of having get-only property on the context class.
My best idea at the moment is to have a scoped RequestContextFactory which has a SetCorrelationId method, and the RequestContext registration simply calls the factory to get an instance. The factory can throw an exception if a new instance is requested before the id is set, to ensure no id-less contexts are created, but it doesn't feel like a good solution.
How can I cleanly register read-only objects with a dependency injection framework, where the value depends on the incoming request?
I only had the idea for a RequestContextFactory as I was writing the original question, and I finally made time to test the idea out. It actually was less code than I expected, and worked well, so this will be my go-to solution now. But, the name factory is wrong. I'm not sure what to call it though.
First, define the context and factory classes. I even added some validation checks into the factory to ensure it worked the way I expect:
public class RequestContext
{
public RequestContext(string correlationId)
{
CorrelationId = correlationId;
}
public string CorrelationId { get; }
}
public class RequestContextFactory
{
private RequestContext _requestContext;
private bool _used = false;
public void SetContext(RequestContext requestContext)
{
if (_requestContext != null || requestContext == null)
{
throw new InvalidOperationException();
}
_requestContext = requestContext;
}
public RequestContext GetContext()
{
if (_used || _requestContext == null)
{
throw new InvalidOperationException();
}
_used = true;
return _requestContext;
}
}
Then, add registrations to your DI container:
services.AddScoped<RequestContextFactory>();
services.AddScoped<RequestContext>(di => di.GetRequiredService<RequestContextFactory>().GetContext());
Finally, the Service Fabric service method looks something like this
public async Task<MyResponse> ProcessAsync(MyRequest request, string correlationId, CancellationToken cancellationToken)
{
using (var scope = _scopeFactory.CreateScope())
{
var requestContext = new RequestContext(correlationId);
var requestContextFactory = scope.ServiceProvider.GetRequiredService<RequestContextFactory>();
requestContextFactory.SetContext(requestContext);
var businessLogic = scope.ServiceProvider.GetRequiredService<BusinessLogic>();
return await businessLogic.ProcessAsync(request, cancellationToken);
}
}
Kestrel middleware could look something like this
public async Task Invoke(HttpContext httpContext)
{
RequestContext requestContext = new RequestContext(Guid.NewGuid().ToString());
var factory = httpContext.RequestServices.GetRequiredService<RequestContextFactory>();
factory.SetContext(requestContext);
httpContext.Response.Headers["X-CorrelationId"] = requestContext.CorrelationId;
await _next(httpContext);
}
Then just do the normal thing and add a RequestContext parameter to the constructor of any class that needs to get the correlation id (or any other info you put in the request context)

Subsequent ServiceStack OAuth attempts failing when using RavenDB (NonUniqueObjectException)

I'm trying to use ServiceStack authentication plugins out of the box along with RavenDB and the RavenUserAuthRepository package.
AppHost
var store = new DocumentStore()
{
ConnectionStringName = "ServiceStackAuthSample"
}
.Initialize();
IndexCreation.CreateIndexes(typeof(RavenUserAuthRepository).Assembly, store);
container.Register(store);
var session = container.Resolve<IDocumentStore>().OpenSession();
container.Register(p => session).ReusedWithin(ReuseScope.Request);
container.Register<IUserAuthRepository>(p => new RavenUserAuthRepository(p.Resolve<IDocumentStore>(), p.Resolve<IDocumentSession>()));
The first authentication attempt via Facebook, GoogleOAuth, Twitter--works as expected. However, if I attempt to re-authenticate, RavenDB doesn't seem to like it and I get the following:
error CodeNonUniqueObjectExceptionmessageAttempted to associate a different object with id 'UserAuths/1'.stack Trace[Auth: 10/21/2013 6:51:04 PM]: [REQUEST: {provider:facebook}] Raven.Client.Exceptions.NonUniqueObjectException: Attempted to associate a different object with id 'UserAuths/1'. at Raven.Client.Document.InMemoryDocumentSessionOperations.AssertNoNonUniqueInstance(Object entity, String id) in c:\Builds\RavenDB-Stable\Raven.Client.Lightweight\Document\InMemoryDocumentSessionOperations.cs:line 778 at Raven.Client.Document.InMemoryDocumentSessionOperations.StoreInternal(Object entity, Etag etag, String id, Boolean forceConcurrencyCheck) in c:\Builds\RavenDB-Stable\Raven.Client.Lightweight\Document\InMemoryDocumentSessionOperations.cs:line 670 at Raven.Client.Document.InMemoryDocumentSessionOperations.Store(Object entity) in c:\Builds\RavenDB-Stable\Raven.Client.Lightweight\Document\InMemoryDocumentSessionOperations.cs:line 608 at ServiceStack.Authentication.RavenDb.RavenUserAuthRepository.CreateOrMergeAuthSession(IAuthSession authSession, IOAuthTokens tokens) at ServiceStack.ServiceInterface.Auth.AuthProvider.OnAuthenticated(IServiceBase authService, IAuthSession session, IOAuthTokens tokens, Dictionary2 authInfo) at ServiceStack.ServiceInterface.Auth.FacebookAuthProvider.Authenticate(IServiceBase authService, IAuthSession session, Auth request) at ServiceStack.ServiceInterface.Auth.AuthService.Authenticate(Auth request, String provider, IAuthSession session, IAuthProvider oAuthConfig) at ServiceStack.ServiceInterface.Auth.AuthService.Post(Auth request) at lambda_method(Closure , Object , Object ) at ServiceStack.ServiceHost.ServiceRunner1.Execute(IRequestContext requestContext, Object instance, TRequest request)
RavenDB searches tell me it may have something to do with my session(s), but I'm not sure what I'm doing wrong here. It could be my understanding of how to manage RavenDB sessions in ServiceStack.
UPDATE
It turns out there was a bug in the v3 branch of ServiceStack.AuthenticationRavenDb.RavenUserAuthRepository::GetUserAuth method. While all other methods were using a private IDocumentSession member, injected by constructor, GetUserAuth was creating a brand new instance of IDocumentSession.
private readonly IDocumentStore _documentStore;
public RavenUserAuthRepository(IDocumentStore documentStore,IDocumentSession session)
{
_documentStore = documentStore;
_session = session;
}
Old
public UserAuth GetUserAuth(string userAuthId)
{
using (var session = documentStore.OpenSession())
{
int intAuthId;
return int.TryParse(userAuthId, out intAuthId)
? session.Load<UserAuth>(intAuthId)
: session.Load<UserAuth>(userAuthId);
}
}
Fixed
public UserAuth GetUserAuth(string userAuthId)
{
using (_session)
{
int intAuthId;
return int.TryParse(userAuthId, out intAuthId)
? _session.Load<UserAuth>(intAuthId)
: _session.Load<UserAuth>(userAuthId);
}
}
This lead to CreateOrMergeAuthSession getting the UserAuth with one session instance (via GetUserAuth), then using the private session member to save the UserAuth, causing the conflict.
Thanks for the tip, David!
I'm not familiar with the ServiceStack DI API you've shown here, but it looks to me like you're creating and registering one DocumentStore (which is good) but then also creating one IDocumentSession and registering just that instance. There should be one unique instance of IDocumentSession for every request, so the container needs to be configured that every request will call the DocumentStore's OpenSession method.

Concurrent requests: session lost after removing session-id from one single request

In my ASP .NET MVC 2 - application, there are several controllers, that need the session state. However, one of my controllers in some cases runs very long and the client should be able to stop it.
Here is the long running controller:
[SessionExpireFilter]
[NoAsyncTimeout]
public void ComputeAsync(...) //needs the session
{
}
public ActionResult ComputeCompleted(...)
{
}
This is the controller to stop the request:
public ActionResult Stop()
{
...
}
Unfortunately, in ASP .NET MVC 2 concurrent requests are not possible for one and the same user, so my Stop-Request has to wait until the long running operation has completed. Therefore I have tried the trick described in this article and added the following handler to Global.asax.cs:
protected void Application_BeginRequest()
{
if (Request.Url.AbsoluteUri.Contains("Stop") && Request.Cookies["ASP.NET_SessionId"] != null)
{
var session_id = Request.Cookies["ASP.NET_SessionId"].Value;
Request.Cookies.Remove("ASP.NET_SessionId");
...
}
}
This simply removes the session-id from the Stop-Request. At the first glance this works well - the Stop-Request comes through and the operation is stopped. However, after that, it seems that the session of the user with the long running request has been killed.
I use my own SessionExpireFilter in order to recognize session timeouts:
public class SessionExpireFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
HttpContext ctx = HttpContext.Current;
// check if session is supported
if (ctx.Session != null)
{
// check if a new session id was generated
if (ctx.Session.IsNewSession)
{
// If it says it is a new session, but an existing cookie exists, then it must
// have timed out
string sessionCookie = ctx.Request.Headers["Cookie"];
if ((null != sessionCookie) && (sessionCookie.IndexOf("ASP.NET_SessionId") >= 0))
{
filterContext.Result = new JsonResult() { Data = new { success = false, timeout = true }, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}
}
}
base.OnActionExecuting(filterContext);
}
}
ctx.Session.IsNewSession is always true after the Stop-Request has been called, but I don't know why. Does anyone know why the session is lost? Is there any mistake in the implementation of the Stop-Controller?
The session is lost because you removed the session cookie. I'm not sure why that seems illogical. Each new page request supplies the cookie to asp.net, and if there is no cookie it generates a new one.
One option you could use to use cookieless sessions, which will add a token to the querystring. All you need to do is generate a new session for each login, or similar.
But this is one of the reasons why session variables are discouraged. Can you change the code to use an in-page variable, or store the variable in a database?

NInject, nHibernate, and auditing in ASP.NET MVC

I am working on an inherited application which makes use of NInject and nHibernate as part of an ASP.NET MVC (C#) application. Currently, I'm looking at a problem with the auditing of modifications. Each entity has ChangedOn/ChangedBy and CreatedOn/CreatedBy fields, which are mapped to database columns. However, these either get filled with the wrong username or no username at all. I think this is because it has been configured in the wrong way, but I don't know enough about nHibernate and NInject to solve the issue, so I hope someone can help. Below some code snippets to hopefully provide sufficient insight in the application.
Creating the session factory and session:
public class NHibernateModule : NinjectModule
{
public override void Load()
{
Bind<ISessionFactory>().ToProvider(new SessionFactoryProvider()).InSingletonScope();
Bind<ISession>().ToProvider(new SessionProvider()).InRequestScope();
Bind<INHibernateUnitOfWork>().To<NHibernateUnitOfWork>().InRequestScope();
Bind<User>().ToProvider(new UserProvider()).InRequestScope();
Bind<IStamper>().ToProvider(new StamperProvider()).InRequestScope();
}
}
public class SessionProvider : Provider<ISession>
{
protected override ISession CreateInstance(IContext context)
{
// Create session
var sessionFactory = context.Kernel.Get<ISessionFactory>();
var session = sessionFactory.OpenSession();
session.FlushMode = FlushMode.Commit;
return session;
}
}
public class SessionFactoryProvider : Provider<ISessionFactory>
{
protected override ISessionFactory CreateInstance(IContext context)
{
var connectionString = ConfigurationManager.ConnectionStrings["DefaultConnectionString"].ToString();
var stamper = context.Kernel.Get<IStamper>();
return NHibernateHelper.CreateSessionFactory(connectionString, stamper);
}
}
public class StamperProvider : Provider<IStamper>
{
protected override IStamper CreateInstance(IContext context)
{
System.Security.Principal.IPrincipal user = HttpContext.Current.User;
System.Security.Principal.IIdentity identity = user == null ? null : user.Identity;
string name = identity == null ? "Unknown" : identity.Name;
return new Stamper(name);
}
}
public class UserProvider : Provider<User>
{
protected override UserCreateInstance(IContext context)
{
var userRepos = context.Kernel.Get<IUserRepository>();
System.Security.Principal.IPrincipal user = HttpContext.Current.User;
System.Security.Principal.IIdentity identity = user == null ? null : user.Identity;
string name = identity == null ? "" : identity.Name;
var user = userRepos.GetByName(name);
return user;
}
}
Configuring the session factory:
public static ISessionFactory CreateSessionFactory(string connectionString, IStamper stamper)
{
// Info: http://wiki.fluentnhibernate.org/Fluent_configuration
return Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008
.ConnectionString(connectionString))
.Mappings(m =>
{
m.FluentMappings
.Conventions.Add(PrimaryKey.Name.Is(x => "Id"))
.AddFromAssemblyOf<NHibernateHelper>();
m.HbmMappings.AddFromAssemblyOf<NHibernateHelper>();
})
// Register
.ExposeConfiguration(c => {
c.EventListeners.PreInsertEventListeners =
new IPreInsertEventListener[] { new EventListener(stamper) };
c.EventListeners.PreUpdateEventListeners =
new IPreUpdateEventListener[] { new EventListener(stamper) };
})
.BuildSessionFactory();
}
Snippet from the eventlistener:
public bool OnPreInsert(PreInsertEvent e)
{
_stamper.Insert(e.Entity as IStampedEntity, e.State, e.Persister);
return false;
}
As you can see the session factory is in a singleton scope. Therefore the eventlistener and stamper also get instantiated in this scope (I think). And this means that when the user is not yet logged in, the username in the stamper is set to an empty string or "Unknown".
I tried to compensate for this problem, by modifying the Stamper. It checks if the username is null or empty. If this is true, it tries to find the active user, and fill the username-property with that user's name:
private string GetUserName()
{
if (string.IsNullOrWhiteSpace(_userName))
{
var user = ServiceLocator.Resolve<User>();
if (user != null)
{
_userName = user.UserName;
}
}
return _userName;
}
But this results in a completely different user's name, which is also logged in to the application, being logged in the database. My guess this is because it resolves the wrong active user, being the last user logged in, instead of the user that started the transaction.
The offending parts are here:
Bind<ISessionFactory>().
.ToProvider(new SessionFactoryProvider())
.InSingletonScope();
Bind<IStamper>()
.ToProvider(new StamperProvider())
.InRequestScope();
And later on:
public class SessionFactoryProvider : Provider<ISessionFactory>
{
protected override ISessionFactory CreateInstance(IContext context)
{
// Unimportant lines omitted
var stamper = context.Kernel.Get<IStamper>();
return NHibernateHelper.CreateSessionFactory(connectionString, stamper);
}
}
public class StamperProvider : Provider<IStamper>
{
protected override IStamper CreateInstance(IContext context)
{
// Unimportant lines omitted
string name = /* whatever */
return new Stamper(name);
}
}
Let's analyze what's going on with the code:
The ISessionFactory is bound as single-instance. There will only ever be one throughout the lifetime of the process. This is fairly typical.
The ISessionFactory is initialized with SessionFactoryProvider which immediately goes out to get an instance of IStamper, and passes this as a constant argument to initialize the session factory.
The IStamper in turn is initialized by the StamperProvider which initializes a Stamper class with a constant name set to the current user principal/identity.
The net result of this is that as long as the process is alive, every single "stamp" will be assigned the name of whichever user was first to log in. This might even be the anonymous user, which explains why you're seeing so many blank entries.
Whoever wrote this only got half the equation right. The IStamper is bound to the request scope, but it's being supplied to a singleton, which means that only one IStamper will ever be created. You're lucky that the Stamper doesn't hold any resources or have any finalizers, otherwise you'd probably end up with a lot of ObjectDisposedException and other weird errors.
There are three possible solutions to this:
(Recommended) - Rewrite the Stamper class to look up the current user on each call, instead of being initialized with static user info. Afterward, the Stamper class would no longer take any constructor arguments. You can the bind the IStamper InSingletonScope instead of InRequestScope.
Create an abstract IStamperFactory with a GetStamper method, and a concrete StamperFactory which implements it by wrapping the IKernel instance. Bind these together InSingletonScope. Have your concrete factory return kernel.Get<IStamper>(). Modify the session factory to accept and hold an IStamperFactory instead of an IStamper. Each time it needs to stamp, use the factory to get a new IStamper instance.
Change the ISessionFactory to be InRequestScope. Not recommended because it will hurt performance and potentially mess up ID generators if you don't use DB-generated identities, but it will solve your auditing problem.
Aaronaught, you're analysis describes exactly what I suspected. However, I found there is a fourth solution which is easier and more straightforward IMHO.
I modified the sessionprovider, such that the call to OpenSession takes an instance of IInterceptor as argument. As it turns out, the event listeners aren't actually supposed to be used for auditing (a bit of a rant, but other than that he is right, according to Fabio as well).
The AuditInterceptor implements OnFlushDirty (for auditing existing entities) and OnSave (for auditing newly created entities). The SessionProvider looks as below:
public class SessionProvider : Provider<ISession>
{
protected override ISession CreateInstance(IContext context)
{
// Create session
System.Security.Principal.IPrincipal user = HttpContext.Current.User;
System.Security.Principal.IIdentity identity = user == null ? null : user.Identity;
string name = identity == null ? "" : identity.Name;
var sessionFactory = context.Kernel.Get<ISessionFactory>();
var session = sessionFactory.OpenSession(new AuditInterceptor(name));
session.FlushMode = FlushMode.Commit;
return session;
}
}

Response is not available in this context when creation cookie's

hi i defined one class to create cookie by received parameter's from user. when i want to add cookie to context i receive an exception.
My Class
public static class ManageCookies
{
public static void Create(string name, string value)
{
HttpCookie cookie = new HttpCookie(name);
cookie.Value = value;
cookie.Expires = DateTime.Now.AddYears(1);
HttpContext.Current.Response.Cookies.Add(cookie);
}
}
Occured Exception: Response is not available in this context.
i know it is connected with the context within the current sub is executing.
i whould suggest my function to pass the current HttpResponse as a parametter to that!
public static class ManageCookies
{
public static void Create(string name, string value, HttpResponse response)
{
HttpCookie cookie = new HttpCookie(name);
cookie.Value = value;
cookie.Expires = DateTime.Now.AddYears(1);
response.Cookies.Add(cookie);
}
public static void PrePareForApplicationStart()
{
Create("somecookie", "somevalue", _context);
}
}
this is correct way? why? and are you have another way?
UPDATE: Oppps! using this way still have First Exception! :(( Help Help
ManageCookies manager = new ManageCookies(this.Context);
manager.PrePareForApplicationStart();
i use above code to send HTTPContext object to defined class. it called from Application_Start event.
and ManageCookies class updated as below:
public class ManageCookies
{
private HttpContext _context;
public ManageCookies(HttpContext context)
{
this._context = context;
}
}
i use this variable (_context) to adding cookies!
The code you provided is not a problem the problem is where you call it from. If you call it after the response is flushed or from a thread other than the one assigned to process your request - you can get all sorts of problems
In response to Sadegh clarification:
This is exactly the point. Application Start happens only once during application lifetime. And I guess you want this cookie to be delivered as a part of every response. In other words on ApplicationStart is not the right place to do it. You can do it at any moment during page lifecycle BEFORE the end of the PreRender

Resources