TempData does not persist on pass to second action - c#

I'm trying to pass TempData through a couple actions, but I can't get it to persist beyond one pass. I've read a ton of questions on StackOverflow about this, but I just can't get their solutions to work. I know TempData only persists for one redirect, but it is suggested that .Keep() or .Peek() should allow it to persist on another redirect. That unfortunately has not worked for me. I have also tried reassigning TempData and straight hard-coding of TempData from the second redirect and it still will not pass through. I'm obviously missing something. My code:
//First redirect
public ActionResult Index(int? userId, int? reportingYear)
{
if (Session["State"] == null)
{
TempData["Timeout"] = "Your session has timed out. Please login to continue.";
return RedirectToAction("LogOff", "Account");
}
}
//Second redirect
[AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
public ActionResult LogOff()
{
//Delete the application cookie and clear session variables
var cookies = Request.Cookies;
List<string> tempCookies = new List<string>();
foreach (string cookie in cookies)
{
if (cookie.ToString() != "quailCoord")
{
tempCookies.Add(cookie);
};
}
foreach (string cookie in tempCookies)
{
HttpCookie deleteCookie = Request.Cookies[cookie];
deleteCookie.Expires = DateTime.Now.AddDays(-1);
Response.Cookies.Add(deleteCookie);
}
Session.Abandon();
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
//When checking that the key exists, it does and enters the if statement to keep the data
if (TempData.ContainsKey("Timeout")
{
TempData.Keep("Timeout");
}
return RedirectToAction("Login");
}
//Third action
public ActionResult Login(string returnUrl)
{
ViewBag.ReturnUrl = returnUrl;
//It does not find any TempData keys
if (TempData.ContainsKey("Timeout"))
{
ViewBag.Timeout = TempData["Timeout"] as string;
}
return View();
}
I have also attempted these in place of the TempData.Keep("Timeout") method:
TempData.Peek("Timeout")
TempData["Timeout"] = TempData["Timeout"]
TempData["Timeout"] = "Your session has timed out. Please login to continue."
None of these methods pass to the Login() action. TempData is always empty upon entering that action. When debugging, the minute I step over the return RedirectToAction("Login") line, the count in TempData turns to 0. What am I missing? Is deleting the cookies a problem?

Because TempData will store data in Session object which might store SessionId in cookie, if you delete that server will create another SessionId (object) for you instead of the original one.
so that if you want to keep TempData to multiple actions we might need to keep SessionId value from the cookie.
From your code, we can try to add
a judgement to check cookie key whether if yes don't delete the cookie.
foreach (string cookie in cookies)
{
if (cookie.ToString() != "quailCoord" && cookie.ToString() != "ASP.NET_SessionId")
{
tempCookies.Add(cookie);
};
}

Related

ASP.Net MVC 5 Cookie loses expiration upon return from the browser

This simple code has left me perplexed.
From this controller action method, I create a cookie , give it an expiration and set it to be HttpOnly. The cookie gets created correctly, added to the Response, looks correct on the browser debugger , however when returned back into the same code after refresh , loses expiration and HttpOnly flag. The cookie itself is still there , but the values are lost. If I watch Request.Cookies["mycookie"] back into the same controller/action method after a trip to the browser, the values are gone - the cookie itself is not deleted though.
If somebody understands this behaviour please explain, what might be happening here-
public class HomeController : Controller
{
public ActionResult Index()
{
if (this.ControllerContext.HttpContext.Request.Cookies["mycookie"] == null)
{
HttpCookie cookie = Response.Cookies["mycookie"];
cookie["mycookie"] = "test";
cookie.Expires = DateTime.Now.AddDays(90);
cookie.HttpOnly = true;
this.ControllerContext.HttpContext.Response.SetCookie(cookie);
}
return View();
}
The problem is this line: return View();
The cookie cannot be set and then read again (server-side) in the same round trip to the server. So, you need to create a second request for the cookie to be available. The simplest way is to force a second request by calling RedirectToAction, although you could use some clever AJAXy way of doing it so it appears to be the same request.
See this post for a working example - here is the part where the cookie is written and deleted.
public class CookieController : Controller
{
public ActionResult Create()
{
HttpCookie cookie = new HttpCookie("Cookie");
cookie.Value = "Hello Cookie! CreatedOn: " + DateTime.Now.ToShortTimeString();
this.ControllerContext.HttpContext.Response.Cookies.Add(cookie);
return RedirectToAction("Index", "Home");
}
public ActionResult Remove()
{
if (this.ControllerContext.HttpContext.Request.Cookies.AllKeys.Contains("Cookie"))
{
HttpCookie cookie = this.ControllerContext.HttpContext.Request.Cookies["Cookie"];
cookie.Expires = DateTime.Now.AddDays(-1);
this.ControllerContext.HttpContext.Response.Cookies.Add(cookie);
}
return RedirectToAction("Index", "Home");
}
}
Ashiquizzaman is also correct in that you are not setting the value of the cookie, but that is only half of the problem.
Please see this code below.
var request=this.ControllerContext.HttpContext.Request;
var response =this.ControllerContext.HttpContext.Response;
//OR
// var request=System.Web.HttpContext.Current.Request;
//var response =System.Web.HttpContext.Current.Response;
if (request.Cookies["mycookie"] == null)
{
HttpCookie cookie= new HttpCookie("mycookie");
cookie.Value = "test";//your problem hear.
cookie.Expires = DateTime.Now.AddDays(90);
cookie.HttpOnly = true;
response.Cookies.Add(cookie);
}
else//need to update your cookies then use this block or not
{
HttpCookie cookie=Request.Cookies["mycookie"];
cookie.Value = "test";//your problem hear.
cookie.Expires = DateTime.Now.AddDays(90);
cookie.HttpOnly = true;
//response.Cookies.SetCookie(cookie);
response.Cookies.Set(cookie);//To update a cookie, you need only to set the cookie again using the new values.
}
Hopefully it's help you.

Pass created data to another controller

I am using ASP.NET MVC Entity Framework and I have a page to insert data
public ActionResult Create()
{
return View();
}
// POST: /Home/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include="id,firstname,lastname,email,guests,guestfirstname,guestlastname,productInterest,occupancyTimeline,isInvestment,timeSlot,dateSlot")] CP_LC_Preview cp_lc_preview)
{
if (ModelState.IsValid)
{
db.Data.Add(cp_lc_preview);
db.SaveChanges();
return RedirectToAction("Confirm", new { info = cp_lc_preview });
}
return View(cp_lc_preview);
}
What I am trying to do is take that data that was just entered and pass it to another controller to display. like a confirmation page.
Here is my method for the confirm page
public ActionResult Confirm()
{
return View();
}
You may consider following the PRG pattern.
PRG stands for POST - REDIRECT - GET. With this approach,After you successfully save the data, you will issue a redirect response with a unique id in the querystring, using which the second GET action method can query the resource again and return something to the view.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include="id,firstname,lastname,email,guests,guestfirstname,guestlastname,productInterest,occupancyTimeline,isInvestment,timeSlot,dateSlot")] CP_LC_Preview cp_lc_preview)
{
if (ModelState.IsValid)
{
db.Data.Add(cp_lc_preview);
db.SaveChanges();
var id = cp_lc_preview.Id;
return RedirectToAction("Confirm", new { id = id });
}
return View(cp_lc_preview);
}
and in your Confirm action method, have id parameter and using the value of that read the record from the db again and use as needed.
public ActionResult Confirm(int id)
{
var d = db.Data.FirstOrDefault(g=>g.Id==id);
// Use d as needed
// to do : Return something
}
TempData
If you do not prefer to have this id in the url, consider using TempData to pass the data. But TempData has a short life span. Once read, the data is gone. TempData uses Session behind the scene to store the data.
TempData["NewItem"] = cp_lc_preview;
return RedirectToAction("Confirm", "controllerName");
and in the Confirm method
public ActionResult actionname()
{
var model=TempData["NewItem"] as CP_LC_Preview
// to do : Return something
}
For your reference
How do I include a model with a RedirectToAction?
You can use TempData for that.
TempData["YourData"] = YourData;//persist data for next request
var myModel=TempData["YourData"] as YourData //consume it on the next request
What is TempData ?
TempData is meant to be a very short-lived instance, and you should only use it
during the current and the subsequent requests only.
Since TempData works this way, you need to know for sure what the next request will be, and
redirecting to another view is the only time you can guarantee this.
Therefore, the only scenario where using TempData will reliably work is when
you are redirecting.This is because a redirect kills the current request , then creates a
new request on the server to serve the redirected view.
Simply said, Asp.Net MVC TempData dictionary is used to share data between
controller actions.
The value of TempData persists until it is read or until the current user’s session times out.
By default, the TempData saves its content to the session state.
TempData values are marked for deletion when you read them. At the end of the request,
any marked values are deleted.
The benefit is that if you have a chain of multiple redirections it won’t cause TempData to
be emptied the values will still be there until you actually use them, then they clear up after
themselves automatically.

Session variable to authenticate users

What should I do if someone tries to access page which is only allowed after being logged in to users. I have done this but it doesn't work, please help:
public ActionResult ViewMyAtdRecord()
{
int EmplID = Convert.ToInt32(Session["Employee"]);
if (Session["Employee"] == "")
{
ViewBag.Message = "NOT AUTHORIZED TO VIEW THIS PAGE";
return View();
}
else
{
IEnumerable<GetAtdRecord_SpResult> MyAtdRecord = DataContext.GetAtdRecord_Sp(EmplID).ToList();
var names = (from n in DataContext.HrEmployees select n).Distinct();
return View(MyAtdRecord);
}
}
Actually session begins here.
public ActionResult AfterLogIn(int EmplID, String EmpPwd)
{
int Num_Rows = (int)DataContext.GetUser_Pwd(EmplID, EmpPwd).First().No_Rows;
if (Num_Rows == 1)
{
Session["Employee"] = EmplID.ToString() ;
ViewBag.Message = Session["Employee"];
}
else
{
ViewBag.Message = "Log-in Failed";
}
return View();
}
First, avoid using session for storing authentication evidence. It makes you vulnerable to session fixation - someone can easily copy and reuse the contents of the ASP.NET_SessionId cookie, particularly if the site is accessible over HTTP and not HTTPS.
Use authentication cookies instead. Yes, cookies have some disadvantages, such as giving malicious users the ciphertext of your authentication cookie. However the FormsAuthenticationProvider has been developed with this in mind. This is why it is generally considered safer to use cookies.
Second, try to avoid using session at all. One of the huge advantages of ASP.MVC is the ability to run sessionless, allowing you to easily scale up your site if needed.
I would recommend using FormsAuthentication (as suggested by #akton) and decorate the actions/controller with the Authorize attribute
If you want to go with Session rather than FormsAuthentication then you could try this out :
public ActionResult ViewMyAtdRecord()
{
int empId = 0;
int.TryParse((string)Session["Employee"], out empId); // parse variable to int
if (empId > 0) // we have an employee id, lets show that record.
{
IEnumerable<GetAtdRecord_SpResult> MyAtdRecord = DataContext.GetAtdRecord_Sp(empId ).ToList();
var names = (from n in DataContext.HrEmployees select n).Distinct();
return View(MyAtdRecord);
}
else // we do not have an employee id, show not authorized message.
{
ViewBag.Message = "NOT AUTHORIZED TO VIEW THIS PAGE";
return View();
}
}

Fixing the action back to an account page in asp.net mvc application

I have an asp.net mvc4 application, in which i have to logout from an account :
if (_fonction == "User")
{
if (_is_admin == true) return RedirectToAction("Index");
else
{
Session["user"] = _u;
return RedirectToAction("Index", "User");
}
}
in the controller User
public ActionResult Index()
{
if (Session["user"] == null) return RedirectToAction("Index", "Home");
return View(Session["user"]);
}
the action Logout
public ActionResult Logout()
{
if (_is_admin) { Session["user"] = null; return RedirectToRoute("Administration"); }
else { Session["user"] = null; return RedirectToAction("Index", "Home"); }
}
i do this : i log in to a user account then i disconnect so i'am in the home page , then i click into the back button of the browser i got the page of the account. When i refresh i returned to the home page. i think that the problem is in the cache and i don't think that make it null is a good idea .
So how can i fix this problem?
You can try to add [OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")] as an attribute to your User controller's Index action to force non-cached results.
But I would strongly suggest to just use AuthorizeAttribute because this will prevent unauthorized web requests done on a specific view. The benefit of using this is that you still give the users the liberty to cache your views and be secured at the same time.
if you do not want to clean your cache then below is the javascript which helps you to hard refresh your page on click of the browser back button
if (window.name != "") { // will be '' if page not prev loaded
window.name = ""; // reset to prevent infinite loop
window.location.reload(true);
}
window.name = new Date().getTime();
put the above "javascript" code on your page. so, it will hard refresh your page.

Cookies logout razor remove

I have an asp application and i need to remove all current session's cookies in the action of logout:
public ActionResult Logout()
{
Upload.Models.CompteModels.Connected = false;
return RedirectToAction("Login", "Account");
}
Now i use a static class CompteModels with a boolean to test if the user is authentifying or not but it's not efficent. so i think that i have to remove all cookies when i logout.
How can i do it?
A static property is shared across all users, so using a static property to determine if a user is logged in will not work correctly, as this would log out all users, or log them in.
You can abandon the session using Session.Abandon or remove a cookie using the HttpResponse.Cookies collection, and write a cookie to it that is expired.
If you mean drop session data and remove the sessions cookies see here for how to do it.
You could create a session variable called LoggedIn or something similar and just clear this in your logout action. Then in your Login action you need to check for this session.
public ActionResult Logout()
{
Upload.Models.CompteModels.Connected = false;
Session.Remove("LoggedIn");
return RedirectToAction("Login", "Account");
}
public ActionResult Login()
{
// check for session var, redirect to landing page maybe?
if(Session["LoggedIn"] == null)
{
RedirectToAction("Home", "Index");
}
else
{
Session.Add("LoggedIn", true);
}
return RedirectToAction("TargetPage", "TargetAction");
}
Just one idea, depends on where you want users to be redirected to and such, TargetPage could be an admin area or something similar.

Categories

Resources