how to detect MVC 4 session end - asp.net-mvc-4

I'm developing my web application using MVC 4, I need to send an email to the user after session time out or after session has been ended by the user. Anyone have any idea how to detect the session end? Thanks

You can use Session_End Event in Global.asax File
protected void Session_End(object sender, EventArgs e)
{
// Write your Logic
}
Be patient. The event should be called, but not necessarily right after the timeout.
You could try from a Browser: Start a session,wait > 1 minute, do a Postback somehow
This should help to verify that the Timeout works and I think you will also see the SessionEnd happening at that time. Otherwise, just wait and start some other sessions. The system will come around o calling it sometime.
For more information
About Session_End Event in MSDN blog
Hope this helps

Related

IHostedService StartAsync called randomely and causes Wrong Timer intervals

I'm using IHostedService back since Asp.net core version 2.1.
I noticed in my logs that StartAsync is called sometimes alot in very messy intervals from 8 mins to one hour and that calls my DoWorkAsync each time.
I have a long process that I don't want it to be recalled on those small intervals It runs in a timer each two hours in normal cases using Timer.
public Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Timed Background Service is starting.");
_timer = new Timer(DoWorkAsync, null, TimeSpan.FromMinutes(2),
TimeSpan.FromHours(2));
return Task.CompletedTask;
}
I'm considering using lock statement but if I made the lock on private object would it be there to lock execution when StartAsync called again.
I'm concerned because the process calls WSI (Webservice) on another server , and afraid it might be recalled before previous call answered and make the other server crashed.
My logging is simply a text file that log times in StartAsync and DoWorkAsync.
I'm running this on Aws windows instance. If the problem might be crashes or self restart how would I see the causes of it. I don't think my simple text file would catch it.
In my opinion the StartAsync() is not supposed to hold a callback object and probably cause your troubles. Pls consider this example: https://learn.microsoft.com/en-us/dotnet/core/extensions/timer-service. It does, what you want to do, but in the right manner.

ASP.NET Core Identity - end sesion on browser close and handeling that event

Is there any way to handle a user closing the browser so by that ending his identity session and handling that event to add a logout date to the user's logs.
the main reason is auditing (user logs). start date (when the user logs in) - end date (when the user logs out or closes the browser)
I tried using onbeforeunload js event, but it went wrong when the user redirects to any page the event will fire.
I tried searching SessionEnd asp.net core events, and determined that it is not implemented in .netcore
Can Signal R accomplish this?
I'm not sure if this is possible or at least it will be a difficult task.
Would it be possible to set the following values:
ExpireTimeSpan = System.TimeSpan.FromHours(1),
SlidingExpiration = true
..in your app.UseCookieAuthentication set up?
This would ensure the session is kept alive as the user is browsing around (requests are being made) but will kill the session in a short period if they are not.

.NET Core alternative to ThreadPool.QueueUserWorkItem

I'm working on implementing the ForgotPassword functionality ie in the AccountController using ASP.NET Identity as in the standard VS 2015 project template.
The problem I'm trying to solve is that when the password reset email is sent, there is a noticeable delay in the page response. If the password recovery attempt does not find an existing account then no email is sent so there is a faster response. So I think this noticeable delay can be used for account enumeration, that is, a hacker could determine that an account exists based on the response time of the forgot password page.
So I want to eliminate this difference in page response time so that there is no way to detect if an account was found.
In the past I've queued potentially slow tasks like sending an email onto a background thread using code like this:
ThreadPool.QueueUserWorkItem(new WaitCallback(AccountNotification.SendPasswordResetLink),
notificationInfo);
But ThreadPool.QueueUserWorkItem does not exist in .NET Core, so I'm in need of some alternative.
I suppose one idea is to introduce an artificial delay in the case where no account is found with Thread.Sleep, but I'd rather find a way to send the email without blocking the UI.
UPDATE: To clarify the problem I'm posting the actual code:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ForgotPassword(ForgotPasswordViewModel model)
{
if (ModelState.IsValid)
{
var user = await userManager.FindByNameAsync(model.Email);
if (user == null || !(await userManager.IsEmailConfirmedAsync(user)))
{
// Don't reveal that the user does not exist or is not confirmed
return View("ForgotPasswordConfirmation");
}
var code = await userManager.GeneratePasswordResetTokenAsync(user);
var resetUrl = Url.Action("ResetPassword", "Account",
new { userId = user.Id, code = code },
protocol: HttpContext.Request.Scheme);
//there is a noticeable delay in the UI here because we are awaiting
await emailSender.SendPasswordResetEmailAsync(
userManager.Site,
model.Email,
"Reset Password",
resetUrl);
return View("ForgotPasswordConfirmation");
}
// If we got this far, something failed, redisplay form
return View(model);
}
Is there a good way to handle this using other built in framework functionality?
Just don't await the task. That's then mostly-equivalent to running all of that code on the thread-pool to start with, assuming it doesn't internally await anything without calling ConfigureAwait(false). (You'll want to check that, if it's your code.)
You might want to add the task to some set of tasks which should be awaited before the server shuts down, assuming there's some appropriate notion of "requested shutdown" in ASP.NET. That's worth looking into, and would stop the notification from being lost due to unfortunate timing of the server being shut down immediately after sending the response but before sending the notification. It wouldn't help in the case where there are problems in sending the notification though, e.g. your mail server is down. At that point, the user has been told that the email is on its way, before you can really guarantee that... just something to think about.

How to get tcmid of currently logged user in Tridion?

private void Subscribe()
{
EventSystem.Subscribe<User, LoadEventArgs>(GetInfo, EventPhases.Initiated);
}
public void GetInfo(User user, LoadEventArgs args, EventPhases phase)
{
TcmUri id = user.Id;
string name = user.Title;
Console.WriteLine(id.ToString());
Console.WriteLine(name);
}
I wrote above code and add the assembly in config file in Tridion server but no console window is coming on login of a user
The event you were initially subscribing to is the processed phase of any identifiable object with any of its actions, that will trigger basically on every transaction happening in the SDL Tridion CMS, so it won't give you any indication of when a user logs in (it's basically everything which happens all the time).
Probably one of the first things which is happening after a user logs in, is that its user info and application data is read. So what you should try is something along the lines of:
private void Subscribe()
{
EventSystem.Subscribe<User, LoadEventArgs>(GetInfo, EventPhases.Initiated);
}
public void GetInfo(User user, LoadEventArgs args, EventPhases phase)
{
TcmUri id = user.Id;
string name = user.Title;
}
But do keep in mind that this will also be triggered by other actions, things like viewing history, checking publish transactions and possibly a lot more. I don't know how you can distinguish this action to be part of a user login, since there isn't an event triggered specifically for that.
You might want to check out if you can find anything specific for a login in the LoadEventArgs for instance in its ContextVariables, EventStack, FormerLoadState or LoadFlags.
Edit:
Please note that the Event System is running inside the SDL Tridion core, so you won't ever see a console window popup from anywhere. If you want to log information, you can include the following using statement:
using Tridion.Logging;
After adding a reference to the Tridion.Logging.dll which you can find in your ..\Tridion\bin\client directory. Then you can use the following logging statement in your code:
Logger.Write("message", "name", LoggingCategory.General, TraceEventType.Information);
Which you will find back in your Tridion Event log (provided you have set the logging level to show information messages too).
But probably the best option here is to just debug your event system, so you can directly inspect your object when the event is triggered. Here you can find a nice blog article about how to setup debugging of your event system.
If you want to get the TCM URI of the current user, you can do so in a number of ways.
I would recommend one of these:
Using the Core Service, call GetCurrentUser and read the Id property.
Using TOM.NET, read the User.Id property of the current Session.
It looks like you want #2 in this case as your code is in the event system.

Disable (Politely) a website when the sql server is offline

I work at a college and have been developing an ASP.NET site with many, many reports about students, attendance stats... The basis for the data is an MSSQL server DB which is the back end to our student management system. This has a regular maintenance period on Thursday mornings for an unknown length of time (dependent on what has to be done).
Most of the staff are aware of this but the less regular users seem to be forever ringing me up. What is the easiest way to disable the site during maintenance obviously I can just try a DB query to test if it is up but am unsure of the best way to for instance redirect all users to a "The website is down for maintenance" message, bearing in mind they could have started a session prior to the website going down.
Hopefully, something can be implemented globally rather than per page.
Drop an html file called "app_offline.htm" into the root of your virtual directory. Simple as that.
Scott Guthrie on the subject and friendly errors.
I would suggest doing it in Application_PreRequestHandlerExecute instead of after an error occurs. Generally, it'd be best not to enter normal processing if you know your database isn't available. I typically use something like below
void Application_PreRequestHandlerExecute(Object sender, EventArgs e)
{
string sPage = Request.ServerVariables["SCRIPT_NAME"];
if (!sPage.EndsWith("Maintenance.aspx", StringComparison.OrdinalIgnoreCase))
{
//test the database connection
//if it fails then redirect the user to Maintenance.aspx
string connStr = ConfigurationManager.ConnectionString["ConnectionString"].ConnectionString;
SqlConnection conn = new SqlConnection(connStr);
try
{
conn.Open();
}
catch(Exception ex)
{
Session["DBException"] = ex;
Response.Redirect("Maintenance.aspx");
}
finally
{
conn.Close();
}
}
}
You could display a message to people who have logged in saying "the site will be down for maintenance in xxx minutes" then run a service to log everyone out after xxx minutes. Then set a flag somewhere that every page can access, and at the top of every page(or just the template page) you test if that flag is set, if it is, send a redirect header to a site is down for maintenance page.
What happens now when the site is down and someone tries to hit it? Does ADO.NET throw a specific exception you could catch and then redirect to the "website down" page?
You could add a "Global.asax" file to the project, and in its code-behind add an "Application_Error" event handler. It would fire whenever an exception is thrown and goes uncaught, from anywhere in your web app. For example, in C#:
protected void Application_Error(object sender, EventArgs e)
{
Exception e = Server.GetLastError().GetBaseException();
if(e is SqlException)
{
Server.ClearError();
Server.Transfer("~/offline.aspx");
}
}
You could also check the Number property on the exception, though I'm not sure which number(s) would indicate it was unable to connect to the database server. You could test this while it's down, find the SQL error number and look it up online to see if it's specifically what you really want to be checking for.
EDIT: I see what you're saying, petebob.
The "offline.html" page won't work if the user was already navigating within the site, or if he's accessing the site from a bookmark/external link to a specific page.
The solution I use is to create a second web site with the same address (IP or host header(s)), but have it disabled by default. When the website is down, a script deactivates the "real" web site and enables the "maintenance" website instead. When it comes back online, another script switches back to the "real" web site.
The "maintenance" web site is located in a different root directory, with a single page with the message (and any required images/css files)
To have the same message shown on any page, the "maintenance" web site is set up with a 404 error handler that will redirect any request to the same "website is down for maintenance" page.
A slightly more elegant version of the DB check on every page would be to do the check in the Global.asax file or to create a master page that all the other pages inherit from.
The suggestion of having an online site and an offline site is really good, but only really applicable if you have a limited number of sites to manage on the server.
EDIT: Damn, the other answers with these suggestions came up after I loaded the page. I need to remember to refresh before replying :)
James code forgets to close the connection, should probably be:
try
{
conn.Open();
}
catch(Exception ex)
{
Session["DBException"] = ex;
Response.Redirect("Maintenance.aspx");
}
finally
{
conn.Close();
}
Thanks for the replies so far I should point out I'm not the one that does the maintenance nor does I have access all the time to IIS. Also, I prefer options where I do nothing as like all programmers I am a bit lazy.
I know one way is to check a flag on every page but I'm hoping to avoid it. Could I not do something with the global.asax page, in fact, I think posting has engaged my brain:
Think I could put in Application_BeginRequest a bit of code to check the SQL state then redirect:
HttpContext context = HttpContext.Current;
if (!isOnline())
{
context.Response.ClearContent();
context.Response.Write("<script language='javascript'>" +
"top.location='" + Request.ApplicationPath + "/public/Offline.aspx';</scr" + "ipt>");
}
Or something like that may not be perfect not tested yet as I'm not at work. Comments appreciated.