I have come across an issue with my mvc razor website where it doesn't refresh properly my page after hitting a timeout.
Here's how I process the whole thing:
My application allows to go through multiple steps. Each step reloads the same view (index.cshtml), the difference being in the session variables. The session variables are used as parameters to make ajax calls to a webservice to get the content to be displayed within the page.
The user can select items displayed within the page and go to the next step. When they click next, it triggers a post (ajax to controller) that will add these items into a session variable so that the selection stays throughout the process and that the user can see all the items selected in the previous steps as he progresses.
I don't want the application to crash when accessing methods (like next, previous, save, etc.) because the session variables have expired.
The way I figured I would prevent that is by decorating my methods in the controller with an attribute that checks if a session variable (isActive) is set, and if it is not, I add a RedirectResult to my filterContext.
public class SessionExpireAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if(filterContext.ActionDescriptor.GetCustomAttributes(typeof(SkipSessionExpireAttribute), false).Any()){
return;
}
if (HttpContext.Current.Session["isActive"] == null)
{
HttpContext.Current.Session["timedOut"] = "true";
filterContext.Result = new RedirectResult("~/Session-Expired", true);
return;
}
}
}
I have set in my routes a route for this url so that it "triggers" the Index method of the controller. (which is the method that returns the view). That way, all session variables would reset, the URL is changed to "Session-Expired" and the page is reload (sending back to the first, default, step).
I tested and it does "trigger" the Index method and goes through it as intended and the Index method returns the view. The problem is, my page never gets reloaded.
Index method is:
[SkipSessionExpire]
[Themed]
public ActionResult Index()
{
// Some code.
return View();
}
So I thought maybe it would be related to caching. I tried several methods found on this wonderful website such as adding:
[OutputCache(Location = System.Web.UI.OutputCacheLocation.None)]
with no real success.
I also tried some "hack" by redirecting to another action which would load a new page (test.cshtml) instead and this page would then redirect to the original view (index.cshtml). The test page worked when I tryed calling it with URL but for some reason, it still won't load the page when redirecting to the action.
My "Next" method (which doesn't get called because it is skipped because of the attribute, which is intended):
[HttpPost]
[Themed]
public JsonResult Next(object item)
{
//Some code.
return Json((string)Session["CurrentUrl"]);
}
The attribute [SessionExpire] is applied to the Controller (so every method within the controller gets it).
I just now tried to put a timeout on the ajax call made to the controller (from the view) in hopes that after tha ajax calls timeouts, it could just redirect to the Index page. It doesn't work (nothing happens on first click on the "next button", and second click bring me one step too far because it is like I went back to first page and then clicked next once).
Also, if I simply refresh the page after the timeout (manually). It realoads the first page with all session variables cleared (which is what I want to do programmatically).
To sum it, up. I would like to know if anyone has experienced something similar or if someone can guide me towards a solution so that I can get a way to refresh the page so that it's sent back to the first (default) step. Also, if I misunderstood something, feel free to tell me! I'm still learning so I'm more than glad to hear constructive feedbacks to help me understand how all of this works.
If you feel I'm too vague or you simply need additional details to help me solve this, just let me know and I'll add whatever is needed. I did quite a bit of research but I might have missed something so if you find something that could be useful, make sure to share.
Thanks!
Related
I have an application where I can use popup views over the normal windows.
For getting the data the user needs to login so I have a token.
Has anyone experience with the way if there is no token, show first a view, then go further where you was heading to?
To make it more clear:
public void Init()
{
if (!CheckToken())
{
Task.Run(() => ShowViewModel<InsertPasswordViewModel>())
.ContinueWith(t => GetData());
}
else
{
//Do your thing
GetData();
}
}
The problem now is that the task is run so view is shown and immediately starts GetData but he has no token and crashes.
Any ideas or fixes are welcome
You shouldn't do navigation in Init. You are essentially navigating away from a page you are in the middle of navigating to. The navigation isn't finished executing when Init is called.
You are not getting a result from ShowViewModel either, so you can't rely on that returning anything when you are done with everything in InsertPasswordViewModel.
What you are probably looking for is navigating to your ViewModel with some kind of parameter telling where you came from. Then let InsertPasswordViewModel use that parameter to navigate back once password is OK.
You probably want to modify the View hierarchy too, which you can do through a custom presenter and a set of presentation hints.
I have a huge website with a huge backend. Somewhere, somehow, there is a Response.Redirect called while trying to open a site (debug environment).
Is there a way to find out, which Response.Redirect causes the redirect?
An certain way would be to set a debug breakpoint on every Response.Redirect in the whole website. But this is a lot of effort.
Another idea I had was to stop at the "ThreadAbortException" (which is thrown by Response.Redirect) with "Debug->Exceptions..". But this doesn't work. Seems like the frame or whatever is no longer executed to get a break on it.
Last attempt was to watch the call-stack. But the stack never gets to the last Response.Redirect because it is a new frame call.
Well, I got an idea which solved my problem but required massive code replacement (which is not a problem with 'Find and replace').
I created an static class:
public static class OwnResponse
{
public static void Redirect(string Url, bool EndResponse = true)
{
HttpContext.Current.Response.Redirect(Url, EndResponse); // set the breakpoint here
}
}
Then I replaced every Response.Redirect in the code with OwnResponse.Redirect. Now I was able to set my breakpoint onto the first line in the class. I called the website and the breakpoint got hit. Then I just watched the call-stack and knew where the redirect occurs.
There is another possible solution, which requires a bit more work. You have to get "Stepping into .NET code" to run. Then you can easily set a break point in the .NET method's first line.
Open the Exception Settings window and search for "threadabort". Check the checkbox for the ThreadAbortException. Now, when a redirect is executed from code, your debug session will enter break mode.
You can use below code in landing page:-
string yourPreviousUrl = Request.UrlReferrer.ToString();
if(!string.IsNullOrEmpty(yourPreviousUrl))
{
//Referrer was found!
}
else
{
//Unable to detect a Referrer
}
As mention in official site :-
HttpRequest.UrlReferrer Property
Gets information about the URL of the client's previous request that
linked to the current URL.
You could try to implement tracing and save the results to a file. The trace data might help you to pinpoint your redirect.
Here is a link with some more background on ASP.NET tracing:
http://www.codeproject.com/Articles/82290/Step-by-Step-Guide-to-Trace-the-ASP-NET-Applicatio
Use fiddler or another http traffic capture tool and capture the network traffic. You should be able to see the request that was initiated and take it from there.
My problem :
if i have a simple post request where i just change some Voodoo items in db on edit:
[HttpPost]
[ValidateAntiForgeryToken]
[SessionStateActionFilter]
public ActionResult Edit(MonkeyJar voodooMonkey)
{
if (!this.service.EditMonkey(voodooMonkey))
return RedirectToAction("Edit",voodooMonkey);
return RedirectToAction("Index");
}
Lets say that EditMonkey takes 1.5 second to respond, while those 1.5 seconds is not over user can spam post requests to the same edit method, so only the lates edit will be saved. I want to prevent that.
I Read alot about this problem. I can ofcourse just diable submit button on submit via jquery, but isnt it a bit of hacky way of solving the problem? arent there any other solutions without disabling the button, and just skip the post request number 2...x and only take into avout the first one?
The double-click issue is typically solved in the UI, yes. If you are creating and have control over the UI, then it's definitely not 'hacky' to prevent double-clicks there using Javascript.
To me it sounds like you are wanting to prevent a situation where someone has either gone around your UI code in some way to perform additional click operations. Whether that's disabling/editing the Javascript, poor browser Javascript support, or something else.
You could do something with session state. If you only permit one edit at a time, you could do something like this (pseudocode):
[HttpPost]
[ValidateAntiForgeryToken]
[SessionStateActionFilter]
public ActionResult Edit(MonkeyJar voodooMonkey)
{
//Prevent double-submit
if (Session.IsEditActive)
{
//TODO: determine if you want to show an error, or just ignore the request
}
Session.IsEditActive = true;
try
{
if (!this.service.EditMonkey(voodooMonkey))
{
//I'm thinking you need this line here in case the Redirect does the Thread.Abort() before the finally gets called. (Is that possible? Too lazy to test. :) Probably a race condition--I'd keep it for a guaruntee)
Session.IsEditActive = false;
return RedirectToAction("Edit",voodooMonkey);
}
}
finally
{
//Ensure that we re-enable edits, even on errors.
Session.IsEditActive = false;
}
return RedirectToAction("Index");
}
Http is stateless. That said, a double post with same data is completely valid (as per the transport).
So, this leaves you with 2 options:
Client side: disable the button, and throttle the request. This is not hacky at all. Is by far the most common solution.
Server side: have some kind of monitor lock per session, so that same session cannot re-enter same block that is already executing.
Comparing both, I would prefer the client side option. is not 100% secure, but 99% can be accepted.
ANSWER Please check your html structure.
I have controller and action.
public class HomeController
{
private Stopwatch controller_action;
public HomeController()
{
controller_action = new Stopwatch();
controller_action.Start();
}
public ActionResult Cars(KendoDataSourceRequest request)
{
controller_action.Stop();
System.Diagnostics.Debug.WriteLine(controller_action.Elapsed);
}
}
I call action Cars from popup window.
My actions:
I open popup and catch 100ms
I close popup
and I open popup and catch 200ms...
And this repeat again and again 100ms, 200, 300...
How check what happen after end constructor and before start action ?
Update
I used miniProfiler I have result:
this is only IE8 and IE9 in chrome all very well
Check out MiniProfiler. It is extremely easy to integrate to MVC, and will provide great info regarding your actions and what MVC is doing behind the scenes, directly in your application's UI.
You can also use it to add custom profiling messages, that can be automatically turned on during debugging but off for production code.
Isn't the MVC framework for .NET open source? You could download the source and find out.
The reason it keeps incrementing, though, is probably because a single instance of the controller is created, and re-used for multiple requests. No, nevermind.
Well, Stopwatch.Stop() does not reset the Elapsed interval.
So it seems like if you close the popup & reopen it you get current time difference from the first Start() call.
Maybe your method should look like:
public ActionResult Cars(KendoDataSourceRequest request)
{
controller_action.Stop();
System.Diagnostics.Debug.WriteLine(controller_action.Elapsed);
controller_action.Restart();
}
I am in a bit tricky situation. I am using JavaScript's PageMethod functionality where I am invoking a PageMethod which works like a gem. However I am having an issue in accessing the HttpContext's state which returns me a value "SYSTEM" for
HttpContext.Current.User.Identity.Name
which is not the actual current User Name.
I know there are couple options like storing HttpContext.Current in a Session or saving Context's state in some other custom container but given a web farm environment I am assuming that this will not work as expected.
Here is the code I am working on with
function MyFunction(){
PageMethod.MyPageMethod();
}
here is the signature of the server method
[System.Web.Services.WebMethod()]
public static void MyPageMethod()
{
// gives me "SYSTEM"
var user = HttpContext.Current.User.Identity.Name;
}
Also if I use the above code to access user name in OnLoad event of the page then it works fine and returns me the CurrentUserName.
I am trying to get the above code to work in an ASP.NET Webform... :)
So I am wondering if there is a way to access the current actual user in page methods without making use of sessions.
Any help will be deeply appreciated.
NiK...
After quite some reading I think I was trying to do something which is not correct as to how page methods work. It gets quite tricky when your application's authentication system is windows based and these page methods when you invoke from JavaScript will not cause a postback and do not invoke the HttpModules. Instead it just calls that page method.
FYI, we had our own custom HTTPModule to handle security.This is even before any other HttpModule occurs and this was not being invoked while calling the page method as we are not doing a postback or even a partial postback (so the whole "niche" of a HTTPPost was missing). Moreover this led to a conclusion that we were making service calls without any authentication and was potentially a big security issue for us.
The bottom line is it was a bad design, well having said that I would like to mention about the solution/workaround we came up with and here is what we did. So, the only option we had is to do a postback keeping the UI alive and we wanted to update a label's message asynchronously and we achieved it by doing a hack using Sys.Application.add_init.
<script language="javascript" type="text/javascript" >
Sys.Application.add_init(function() {
Sys.WebForms.PageRequestManager.getInstance().add_endRequest(endRequest);
});
function beginProcess() {
processCurrentItem();
}
var currentItem = 0;
function processCurrentItem() {
if (currentItem < 5) {
currentItem = currentItem + 1;
__doPostBack('updatePanel', currentItem);
}
}
function endRequest() {
processCurrentItem();
}
</script>
The markup we had in place was pretty simple with a label in the update panel and a button that invokes the "beginProcess()" function. Finally in the OnLoad we had the following code in place
protected override void OnLoad(EventArgs e)
{
if (this.IsPostBack)
{
this.lblLabel.Text = "text you may wanna update with";
// Call the Method you may wanna call
// also you may use Request["__EVENTARGUMENT"] to know who caused the
// postback and Request["__EVENTTARGET"] to access the argument you may
// have passed in.
}
}
And this solution is no longer using the JavaScript Page methods. And based on this solution if anyone thinks I am missing something here or think there is any other other way of doing this then do update this post with your suggestions.
NiK