ASP.NET MVC Session state is lost - asp.net

I have a controller and two actions in it:
public ActionResult Index()
{
this.Session.Add("Boo", "Foo");
return View();
}
public ActionResult Details()
{
Debug.WriteLine(this.Session["Boo"]);
return View();
}
Then on the Index View I have a link
<div class="form-group">
#Html.ActionLink("Details", "details")
</div>
When Index action is invoked I set session variable "Boo", then in the Index View, I click on the Details link and when I get to Details action, Session ID is different from what it was in the Index action, and obviously this.Session["Boo"] is null.
Any ideas why is it happening?
Thanks!

This happens when cookies are disabled in a browser. Therefore, the session does not preserve the state.
Even with browsers that do support cookies, some users prefer to turn off cookie support. If your application needs to be responsive to browsers that don't support cookies, you cannot use ASP session management.
In this case, you must write your own mechanism to pass information from page to page in your application. There are two general ways to do this:
Add parameters to a URL's query string.
Add hidden values to a form.
For more information see documentation: Using Cookies to Maintain Sessions in ASP

Related

Detecting redirect to a different URL

I am using ASP.NET MVC with AngularJs framework. In my home controller I'm checking if there is a valid license and if not, I'm re-directing to a page to import license with this code:
public ActionResult Index()
{
var retVal = _licenseProvider.ValidateExistingLicense();
if (!retVal.Item1)
{
Response.Redirect("DataMaintenance/ImportLicenses", true);
return View("DataMaintenance/ImportLicenses");
}
So, in my ImportLicenses controller I want to detect that I was re-directed vs. called from the menu. I found an older thread about redirecting to a different action, but that solution doesn't apply. What are my options here?
You have a couple of options here:
Add a query string parameter in the ImportLicenses action that
determines whether the user got here via a redirect or the menu
Set a TempData variable before redirecting your user to the
ImportLicenses action.
Use a Session variable
Read this for more information about passing data between Action methods.
It really depends on your constraints, there are a number of options. Without knowing much about your project. My first suggestion would be to drop a cookie before redirecting, then when the request comes in to the ImportLicenses action you can check for the check, and delete it, but include whether or not the cookie was found in your view model so you can reflect this in the UI.
There are other options like using session state, or a query string parameter. A querystring parameter could be just as effective as the cookie idea i mentioned above, and it would be a bit cleaner.
If you can provide more information about your use case, I may be able to expand my answer.
Hope this helps.
In the meanwhile I decided to do a little bit of cheating, but it worked. In the menu I changed the original call to this
dataMaintNodes.SubNodes.Add(new MenuMapNode() { LabelKey = "importLicense", Action = "ImportLicenses", Controller = "ImportLicenses", Area = "DataMaintenance", Icon = "", Roles = "IMPORTLIC" });
In other words, instead of normal Index action I introduced a different action. I added that new action to my controller and just set the ViewBag property to false when called from the menu and to true in the Index action that is called by Redirect. So, it's a cheating, but it works. I only wanted to display Cancel button on that modal dialog when I am calling from the regular menu and no Cancel button when called from the home controller. Now, my second problem is that Modal Dialog doesn't really prevent from escaping from it and continuing working with the application. So, we may want to introduce more logic.

Asp.net Mvc custom content managed regions like webforms user controls

In asp.net web forms we have user controls as reusable components for pages. These user controls can be passed values externally through public properties e.g. on a web form we can drop this user control to display a text which came from db (like content managed system) by setting key as public property to this user control and it will pull the value. ( this key, value can be stored in application cache as list or dictionary to avoid DB round trips).
I want to implement same idea in asp.net mvc, but new to it. Any expert suggestion to implement same idea will be very helpful? Thanks
The concept of a user control is a PartialView; there are two ways to use a partial view. The first is to define a partial in the view itself:
#Html.Partial("NameOfViewInControllerFolder", ModelForPartialview)
The second way is have an action method that returns a partial view:
public ActionResult X()
{
return PartialView("NameOfView");
}
And from your view use:
#Html.Action("X", "ControllerName")
And that will call the action method, and insert the results. To ensure that action is only called within the a view, you can use the [ChildActionOnly] attribute.
If an action method, you can use JQuery to request it via AJAX, and load the results into a view:
$.ajax({
type: "GET|POST",
url: "#Url.Action("X", "ControllerName")",
success: function(d) { /* d is HTML */ });

ActionFilter does not execute with cached output

I am using the OutputCache attribute to cache my home page content
[OutputCache(CacheProfile = "Default")]
public ActionResult Index()
{
.....
}
I also have a custom global attribute set-up that checks each request for a valid browser type
GlobalFilters.Filters.Add(new BadBrowserAttribute());
The OnActionExecuting fires as needed during the first request, but not again until the cache expires.
I had thought the Order value of the attributes may help but it doesn't seem to have made any difference.
Is it possible to get my custom attribute to fire every time for cached content?
It's impossible for your action to fire everytime. When you use caching (as in your code), on subsequent request results will be returned from cache, so action won't be executed.
A workoround might be to implement custom caching inside your action filter. Something like this

Detecting is a user is an administrator (or any type) per page, asp.net mvc default membership provider

I'm building a web application, and I would like to make certain functions on the front end visible when a user is logged in as an administrator.
I am using the default membership provider, but I have an extension table called userFields which i use for storing user settings.
I have just added a UserType field to this table, and am now considering how will be best to pick this up on the pages.
I don't really want to have to add extra code to each controller method to get this information out of the db, and pass it through.
Is there a better way to do this, either using the built in roles settings in asp.net membership, OR is there a cool way I can expose this information from my db, in the same way user.identity.name works or (User.Identity.Name).ProviderUserKey;
As long as you are using the standard authentication method that places the user object in the context it shouldn't be too hard. In your view, use Html.RenderPartial and the appropriate controller to accomplish what you want like this...
/Home/Index View:
<div id="menu">
<ul>
<li>Page 1</li>
<li>Page 2</li>
<% Html.RenderPartial("/Home/AdminView"); %>
</ul>
</div>
/Home/Admin View:
<li>Admin 1</li>
<li>Admin 2</li>
/Shared/Empty View:
<!--Empty view for non-viewable admin stuff-->
/Home Controller:
public ActionResult AdminView()
{
//Check if user is admin
//This should be your own logic... I usually have a few static methods that the
//Users or User object has to assist with checking
if (Models.User.IsAdmin(HttpContext.User.Identity.Name))
{
return View();
}
else
{
return View("Empty");
}
}
That should work... It's how I do it. If it's wrong, hopefully someone smarter posts a better answer so we can both learn
EDIT:
Just thought of something else... If your user object implements IPrincipal, you could extract it from the context, cast it to your user type and have the information right in the user class...
Kind of like this
class User : IPrincipal
{
//Implement IPrincipal stuff
public string Role { get; set; }
}
Then the admin view logic could look like this:
public ActionResult AdminView()
{
//Check if user is admin
//This should be your own logic... I usually have a few static methods that the
//Users or User object has to assist with checking
if ( ((Model.User)HttpContext.User).Role =="Admin" )
{
return View();
}
else
{
return View("Empty");
}
}
Use the asp.net roles - then you can use authorization attributes on your controllers to ensure that the user has the relevant roles set.
See this question.
This solves the problem of only allowing certain actions/controllers to be accessible to certain users.
It doesn't solve the problem of only making certain features 'visible'. To do that you will need to either create different views (or partial views) and your controller(s) chooses which view to render based on roles - or you write role specific logic directly into your view.
Of course if you store flags for users in a separate table you can do all this based on your own data. Write your own authorization attribute (i.e. an authorization filter), and use your own data to drive views - just pass it to the view as part of the view data - just like any other part of the model.
You say you don't want to write code to pass the data through your controller to the view - but this is kind of what controllers are for - it's the reason they exist. You could write a static method - or a service of some type that access the DB directly from the view, bypassing the controller, but then you might as well just go back to webforms.

How not cache an ASP.NET user control?

I'm using OutputCache in my page that has a user control, but I don't want to cache this specific user control because it's related to a user login (if I access the page, I see the page as if I were authenticated with another user).
How can I do that?
Personally I use the VaryByCustom attribute to give logged in and logged out users different cached page views:
<%# OutputCache VaryByCustom="IsLoggedIn" Duration="30" VaryByParam="*" %>
then in global.asax you put
public override string GetVaryByCustomString(HttpContext context,
string arg)
{
if (arg == "IsLoggedIn")
{
if (context.Request.IsAuthenticated)
{
return "Logged in: " + context.User.Identity.Name;
}
else
{
return "Not Logged In";
}
}
else
{
return base.GetVaryByCustomString(context, arg);
}
}
I am just going to throw this out there. How about the substitution control?
http://msdn.microsoft.com/en-us/library/ms228212.aspx
According to msdn website:
The Substitution control lets you
create areas on the page that can be
updated dynamically and then
integrated into a cached page. ...
The Substitution control offers a
simplified solution to partial page
caching for pages where the majority
of the content is cached. You can
output-cache the entire page, and then
use Substitution controls to specify
the parts of the page that are exempt
from caching.
I have never used the substituion control personally, but I just happened to look it up the other day, and it sounded like it can somehow inject updated content into an otherwise cached page output.
You can cache a page and you can cache a user control, but you can't cache a page except for a user control. When the user control runs the entire page has to run. You have to make the output cache for the page recognise the different users.
You can use VaryByHeader="Cookie" to cache the page for each set of cookies if the user identity is stored in a cookie. You can use VaryByCustom="SomeString" and implement a check for SomeString to do your own check for user identity in the GetVaryByCustomString method in Global.asax.
You can create a cache filter : http://weblogs.asp.net/rashid/archive/2008/03/28/asp-net-mvc-action-filter-caching-and-compression.aspx
Check inside this filter if the user is logged or not.

Resources