Session Cookie not overwriting - c#

I have a asp.net MVC project that when someone visits the home page it presents a number of buttons depending on which departments they work for.
They can then select the department they want to log in under.
The following controller then sets a session cookie with the appropriate contractId.
However if they login again under a different department the cookie doesn't get overwritten with the new contractId
public ActionResult SetContractId(int contractId)
{
Session["LoggedContractId"] = contractId;
return RedirectToAction("IndexLoggedIn");
}
I call the above from the buttons which are displayed in the view using the following:
#foreach (var item in Model)
{
<div class="col-lg-offset-4 col-md-8" style="margin-bottom: 25px">
#Html.ActionLink("Login under this Contract", "SetContractId", new { contractId = item.ContractId }, new { #Class = "btn btn-primary" }) <b>#Html.DisplayFor(modelItem => item.DepartmentName)</b>
</div>
}
I use this cookie value to set up the system for them. I use the session variable like this:
public ActionResult StartDebrief()
{
if (Session["LoggedContractId"] == null)
{
return RedirectToAction("IndexLogin", "Home", null);
}
else
{
var user = User.Identity.Name;
string userName = user.Substring(7);
var creator = Peopledb.People.FirstOrDefault(x => x.Username == userName);
var creatorContractId = (int)Session["LoggedContractId"];
//Use creatorContractId in the viewmodel and other areas
return View(viewModel);
}
}
Any ideas?

Your code to update the session information looks good in my opinion. This means that the session data should be updated correctly. However, it is possible the call to this function is not rightly in place. Try debugging the function SetContractId(). Where do you call this function, after a new login?

Related

Why would an HTML.Action cause the Home/Index method to be called repeatedly only when user is not authenticated?

In _Layout.cshtml, I use the following call:
#Html.Action("GetCurrentAnnouncement", "Announcement", new { isUserLoggedIn = User.Identity.IsAuthenticated })
It calls this method:
public async Task<PartialViewResult> GetCurrentAnnouncement(bool isUserLoggedIn)
{
var announcement = await _announcementService.GetCurrentAnnouncement(isUserLoggedIn);
if (announcement == null) return null;
var model = new AnnouncementDisplayViewModel();
model.AnnouncementId = announcement.AnnouncementId;
model.AnnouncementText = announcement.AnnouncementText;
model.DisplayToAll = announcement.DisplayToAll;
model.HyperText = announcement.HyperText;
model.Url = announcement.Url;
model.StartDate = announcement.StartDate;
model.EndDate = announcement.EndDate;
return PartialView("~/Views/Shared/_Announcement.cshtml", model);
}
This works just fine when a user is already authenticated. The HomeController's Index method is called just once, I can break into the GetCurrentAnnouncement method and watch it do its' thing perfectly, and the _Announcement.cshtml is displayed just fine.
_Announcement.cshtml:
#model Announcement.AnnouncementDisplayViewModel
#{
Layout = null;
}
<div class="row">
<div class="col-md-12 announcement">
<span class="full-announcement-text">#(Html.DisplayFor(m => m.AnnouncementText))</span>
#if (Model.HyperText != "" && Model.HyperText != null)
{
<br />
#Model.HyperText
}
</div>
</div>
The problem is, if I log out, the Home/Index method gets called repeatedly until Chrome warns me briefly about too many redirects and then I get an HTTP Error 404.15 - Not Found
The request filtering module is configured to deny a request where the query string is too long. with a ridiculous looking querystring http://localhost:54578/?ReturnUrl=%2F%3FReturnUrl%3D%252F%253FReturnUrl%253D%25252F%25253FReturnUrl%25253D%2525252F%2525253FReturnUrl%2525253D%252525252F%252525253FReturnUrl%252525253D%25252525252F%25252525253FReturnUrl%25252525253D%2525252525252F%2525252525253FReturnUrl%2525252525253D%252525252525252F%252525252525253FReturnUrl%....
In that situation, I never hit the GetCurrentAnnouncement method once. Just Home/Index over and over until it is borked. I cannot for the life of me figure out what might be going on. It doesn't matter if I make the GetCurrentAnnouncement method non-async and return a static viewmodel, with no isUserLoggedIn param passed to it, or anything else I have tried. Here's HomeController's Index method. It always gets to the last line where it returns the HomePage view (over and over):
public ActionResult Index()
{
//Healt check to see if we have connection to the APIs and the DB
var healtCheck = _heartbeatService.GetHeartBeat();
if (healtCheck == null || healtCheck.Result == null || (!healtCheck.Result.Success))
{
return View("Error");
}
var user = System.Web.HttpContext.Current.User;
bool userAuthenticated = (user != null) && user.Identity.IsAuthenticated;
if (userAuthenticated)
{
if (user.IsInRole(Roles.WebAdmin) || user.IsInRole(Roles.Maintenance))
{
return RedirectToAction("Index", "Shop");
}
if (user.IsInRole(Roles.Shop))
{
return RedirectToAction("Index", "Referral");
}
}
var viewModel = new LoginViewModel();
return View("HomePage", viewModel);
}
I did find this somewhat interesting line in HomePage view, but the form never gets submitted and everytime I hit it, ViewBag.ReturnUrl is null, so IDK:
#using (Html.BeginForm("Login", "Account", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
Found it! I should have posted the whole AnnnouncementController; it had an [Authorize] attribute at the top level. By using an [OverrideAuthorization] attribute on the GetCurrentAnnouncement method (which needs called regardless of whether a user is logged in or not), it solved all of my headaches. Thanks for all the clues, MVC. The more I get to know you, the less I like you, and I did not like you to begin with .

How to run LogOut Action In Home Controller that can not run by ActionLink?

My Home Controller Like this:
public class HomeController : Controller
{
....
[ActionName("Login")]
public ActionResult Login()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(string UserName, string Password)
{
try
{
var query = user_Repository.Login(UserName, Password);
if (query != null)
{
Session["UserName"] = query.UserName;
Session["IsAdmin"] = query.IsAdmin;
return RedirectToAction("Index", "News");
}
ViewBag.ErrorMessage = "نام كاربري يا رمز عبور صحيح نمي باشد";
return View();
}
catch (Exception e)
{
ViewBag.ErrorMessage = " خطا!!! ";
return View();
}
}
public ActionResult LogOut()
{
if (Request.Cookies["user"] != null)
{
var user = new HttpCookie("user")
{
Expires = DateTime.Now.AddDays(-1),
Value = null
};
Response.SetCookie(user);
Response.Cookies.Clear();
}
if(Session["UserName"] != null)
{
Session["UserName"] = null;
Session["IsAdmin"] = null;
}
Session.Clear();
return RedirectToActionPermanent("Index");
}
}
When i Use ActionLink in _Layout to Run Logout Action in Home Controller , this ActionLink Instead of executing Logout Action, Login will be executed!!!
My ActionLink Like this:
#Html.ActionLink("Log Off", "LogOut", "Home")
where is my wrong??? why #Html.ActionLink("Log Off", "LogOut", "Home") run Login Action????????????????????
i use this code for run that LogOut Action in my _Layout and this code work Correct...
#using (Html.BeginForm("LogOut", "Home", FormMethod.Post, new { role = "form" }))
{
#Html.AntiForgeryToken()
<input type="submit" value="خروج" class="btn btn-primary" />
}
This is a text that stackOverflow lets me send the text because stackOverflow says i should give more explanations and say "it looks like your post is mostly code; please add some more details"!!! i dont have any more detail.... i explain all of that ;)
Do not put your Logout action in a Form. Your ActionLink should work correctly if you have given the correct parameters to it.
Your ActionLink would look like this:
<a href='#Url.Action("LogOut", "Home")'>Log Out</a>
And your Controller action result would look like this:
public ActionResult Logout()
{
if (Request.Cookies["user"] != null)
{
var user = new HttpCookie("user")
{
Expires = DateTime.Now.AddDays(-1),
Value = null
};
Response.SetCookie(user);
Response.Cookies.Clear();
}
if(Session["UserName"] != null)
{
Session["UserName"] = null;
Session["IsAdmin"] = null;
}
//Session.Abandon();
//Session.Clear();
return RedirectToAction("Login", "Home");
}
Use RedirectPermanent if the resource has been moved permanently and will no longer be accessible in its previous location. Most browsers will cache this response and perform the redirect automatically without requesting the original resource again.
Use Redirect if the resource may be available in the same location (URL) in the future.
So in your case Redirect would be the option since this would be hit by any user logging out of your system.
Session.Abandon removes all the objects stored in a Session. If you do not call the Abandon method explicitly, the server removes these objects and destroys the session when the session times out. It also raises events like Session_End
Session.Clear removes all keys and values from the session-state collection.
Think of it like this: Session.Clear can be compared to removing all items from a cart, while Session.Abandon is more like abandoning the entire cart itself.
You can use either of these two methods for destroying or clearing your Session. Currently you are doing a Clear explicitly by assigning your UserName and IsAdmin to null.

How do I perform 2 different ActionResults in the same controller within the same view?

I am trying to remove a role from my application user on ActionLink click and when it is successful send a Viewbag message. The issue I am having currently is that when I try to remove the user from the role, the page is being refreshed. The reason for this is because I want the admin to be able to get a list of roles he is requesting and delete from the same page, to keep the application lean. Here is my controller action:
public ActionResult DeleteRoleForUser()
{
var list = _context.Roles.OrderBy(r => r.Name).ToList().Select(rr => new SelectListItem { Value = rr.Name.ToString(), Text = rr.Name }).ToList();
ViewBag.Roles = list;
return RedirectToAction("GetRoles");
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult DeleteRoleForUser(string UserName, string RoleName)
{
var account = new AccountController();
ApplicationUser user = _context.Users.FirstOrDefault(u => u.UserName.Equals(UserName, StringComparison.CurrentCultureIgnoreCase));
if (account.UserManager.IsInRole(user.Id, RoleName))
{
account.UserManager.RemoveFromRole(user.Id, RoleName);
ViewBag.Message = "Role removed from this user successfully!";
return View("GetRoles");
}
var list = _context.Roles.OrderBy(r => r.Name).ToList().Select(rr => new SelectListItem { Value = rr.Name.ToString(), Text = rr.Name }).ToList();
ViewBag.Roles = list;
return View("GetRoles");
}
Here is the remove link on my view
#Html.ActionLink("Remove Role", "DeleteRoleForUser", new{controller = "Role"})
I have the DeleteRoleForUser and the GetRoles controllers working properly/separately on different views by themselves with DeleteRoleForUser using a listbox to display all roles in the database, however, as I mentioned earlier I would like to combine the two pieces of functionality.
First, your action link will cause your browser to issue a GET request, not a POST. So your [HttpPost] attribute will need to change accordingly.
Second, you aren't passing in any parameters into the method. Your link will need to change to
#Html.ActionLink("Remove Role", "DeleteRoleForUser", new{ controller = "Role", UserName="username", RoleName="role"})

How to maintain previously selected values in controls once validation fails in Asp.net mvc

I have a partialview _Psite which contains two dropdownlist and a text box, second one is based one first as Jsonresult (cascading dropdowns). So now suppose if customer select values in first dropdownlist, second one will load based on jquery and json.Then when he enter wrong values in text box validation fails(Session["username"] == null) it will display the same partial view after post in order to reenter .The problem now i am facing is the two dropdownlist is resetting in to default values.I have googled but couldn't find a solution
Following is view of _Psite
#using (Ajax.BeginForm("_firstGridAll", "mnis", new AjaxOptions { HttpMethod = "POST", UpdateTargetId = "PsitegridContent" }))
{
<div style="float: left">
#Html.DropDownList("REGION_CODE", (SelectList)ViewBag.Categories, "Select region code")
#Html.ValidationMessageFor(m => m.REGION_CODE)
</div>
<div class="tested">
<select id="GEO_ZONE_CODE" name="GEO_ZONE_CODE"></select>
</div>
<div class="tested">
#Html.TextBoxFor(m => m.PSITE_ID)
#Html.ValidationMessageFor(m => m.PSITE_ID)
</div>
<div class="Testedagain">
<input type="submit" value="Search" />
</div>
}
Controller is
public ActionResult _Psite()
{
if (TempData["values"].ToString() == "value persists")
{
ViewBag.change = true;
// ViewBag.Categories = TempData["EnterUniqueKeyHere"];
// return PartialView("_failValidation");
}
var categories = db1.MN_PSITE.Select(c => new
{
REGION_CODE = c.REGION_CODE,
CategoryName = c.REGION_CODE
}).Distinct().ToList();
ViewBag.Categories = new SelectList(categories, "REGION_CODE", "CategoryName");
ViewBag.error = false;
ViewBag.change = false;
return PartialView();
}
and the controller for validating data is following
[HttpPost]
public ActionResult _firstGridAll(string REGION_CODE, string GEO_ZONE_CODE, string PSITE_ID)
{
if (ModelState.IsValid == true)
{
Session["username"] = null;
var items = db1.MN_PSITE.Where(x => x.REGION_CODE == REGION_CODE).Where(y => y.GEO_ZONE_CODE == GEO_ZONE_CODE).Where(z => z.PSITE_ID == PSITE_ID);
//db1.MN_PSITE.Where(x => x.REGION_CODE == Region).Where(y => y.GEO_ZONE_CODE == GeoZONE).Where(z => z.PSITE_ID == Psiteid);
foreach (var it in items)
{
Session["username"] = it.PSITE_SLNO.ToString();
return PartialView(items.ToList());
}
if (Session["username"] == null) //validation fails
{
TempData["error"] = "value doesnot exisit,please renter the details";
return RedirectToAction("_Psite");
}
}
//count = 0;
return PartialView(db1.MN_PSITE.ToList());
}
UPDATE
i am using Entityframework generated classes as model no view viewmode ,do here 'db' is an instance of entity class
If you were posting a view model into your action instead of individual parameters, then you would be able to simply pass that model back out in your partial at the end of the action.
It will only be a small class with a few properties, so will take a minute to create. Do it. It will give you what you want.

Reloading View does not update all of the controls on the form

I have a view the contains an #Html.DropDownListFor. When the form/view loads, if one of the model properties has values (IEnumerable), then it will create a bunch of divs with the corresponding data. If that property does not have any values (aka Count() == 0), then it is supposed to show a single button on the form (which will create the data for that property).
So, when the user selects one of the options from the Dropdown, I fire an ajax call to the exact same action method that populated the current form/view, but this time, it sends a value in the id field.
I have a breakpoint in my action method and I verified that it is getting hit, and it has the correct parameter value and creates the correct data for the model that gets passed to the view, BUT...when the model is sent to the view to re-populate, NONE of the items/controls on the form change. I even put breakpoints in the cshtml file and it's going through there with the correct data also.
So, here's my controller:
public ActionResult Index(int? id)
{
var seasonId = id;
if (seasonId == null)
{
var player = _playerRepository.Query().FirstOrDefault(p => p.PlayerId == _userIdentity.PlayerId);
if (player.DefaultSeasonId != null)
seasonId = (int)player.DefaultSeasonId;
else
{
return View(new ScheduleModel
{
Player = player,
AvailableSeasons = _seasonRepository.Query().Select(s => s)
});
}
}
return View(CreateScheduleModelForSeason((int)seasonId));
}
Here's the beginning of my view:
#model LeagueManager.Models.ScheduleModel
#{
ViewBag.Title = "Schedule(s)";
}
<div class="row">
#Html.LabelFor(m => m.AvailableSeasons)
#Html.DropDownListFor(m => m.SelectedSeasonId, new SelectList(Model.AvailableSeasons, "SeasonId", "SeasonName"), new { id = "seasonSelect" })
</div>
<form method="post" action="Schedule/GenerateSchedule">
<h2>The Season's Schedules/Weeks and Matchups</h2>
<div>
<div>
#if (Model.SchedulesAndMatches == null || (Model.SchedulesAndMatches != null && !Model.SchedulesAndMatches.Any()))
{
<input type="submit" class="btn btn-primary" value="Generate Schedule" />
}
</div>
And here's the ajax call:
#* Season Selector *#
$('select#seasonSelect').change(function () {
var selectedSeasonId = $(this).val();
$.ajax({
url: '/Schedule/Index',
data: { id: selectedSeasonId }
});
});
Again, all of the actual code is working, it's just not re-rendering the view...
Example: when calling the ActionResult method with an id = 1, it loads the entire schedule. When switching to id = 2 via the dropdown (then getting called again via the ajax), it stays with the same schedule.
On the flip side: when calling the ActionResult method with an id = 2, it loads the single button. When switching to id = 1 via the dropdown, it re-populates the correct data in the model, but the view/form does not reflect the new information.
Please help!
When you call action using ajax you can’t return view, you have to return json data.
So your solution is remove ajax call and set window.location with your post url..
#* Season Selector *#
$('select#seasonSelect').change(function () {
var selectedSeasonId = $(this).val();
window.location = '/Schedule/Index/' + selectedSeasonId;
});

Categories

Resources