HttpContext.Current.Session is null in MVC 3 application - asp.net-mvc-3

I have a bilingual MVC 3 application, I use cookies and session to save "Culture" in Session_start method inside Global.aspx.cs file, but direct after it, the session is null.
This is my code:
protected void Session_Start(object sender, EventArgs e)
{
HttpCookie aCookie = Request.Cookies["MyData"];
if (aCookie == null)
{
Session["MyCulture"] = "de-DE";
aCookie = new HttpCookie("MyData");
//aCookie.Value = Convert.ToString(Session["MyCulture"]);
aCookie["MyLang"] = "de-DE";
aCookie.Expires = System.DateTime.Now.AddDays(21);
Response.Cookies.Add(aCookie);
}
else
{
string s = aCookie["MyLang"];
HttpContext.Current.Session["MyCulture"] = aCookie["MyLang"];
}
}
and second time it goes into the "else clause" because the cookie exists; inside my Filter, when it tries set the culutre, Session["MyCulture"] is null.
public void OnActionExecuting(ActionExecutingContext filterContext)
{
System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(HttpContext.Current.Session["MyCulture"].ToString());
System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.CreateSpecificCulture(HttpContext.Current.Session["MyCulture"].ToString());
}

Why are you using HttpContext.Current in an ASP.NET MVC application? Never use it. That's evil even in classic ASP.NET webforms applications but in ASP.NET MVC it's a disaster that takes all the fun out of this nice web framework.
Also make sure you test whether the value is present in the session before attempting to use it, as I suspect that in your case it's not HttpContext.Current.Session that is null, but HttpContext.Current.Session["MyCulture"]. So:
public void OnActionExecuting(ActionExecutingContext filterContext)
{
var myCulture = filterContext.HttpContext.Session["MyCulture"] as string;
if (!string.IsNullOrEmpty(myCulture))
{
Thread.CurrentThread.CurrentUICulture = new CultureInfo(myCulture);
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(myCulture);
}
}
So maybe the root of your problem is that Session["MyCulture"] is not properly initialized in the Session_Start method.

Related

How to make auto logout page in mvc after a some time

Title- asp.net-mvc5 Auto logout How to make form auto logout after sometime in asp.net-mvc5 and redirect automatically to login page
You need to create a session variable on the Login method.
The session will be created by Session["Userid"]=Userid;. Then you need to create custom attribute to check session timeout.
Steps you need to follow are:
Create a session variable in login() (Post method)
Create a class file in your MVC project.
Copy and paste below code in that file.
public class SessionTimeOutAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
Controller controller = filterContext.Controller as Controller;
HttpContext httpContext = HttpContext.Current;
var rd = httpContext.Request.RequestContext.RouteData;
string currentAction = rd.GetRequiredString("action");
string currentController = rd.GetRequiredString("controller");
if (HttpContext.Current.Session["UserId"] == null)
{
filterContext.Result = new RedirectResult("~/Account/Login?ReturnUrl=" + currentController + "/" + currentAction);
return;
}
base.OnActionExecuting(filterContext);
}
}
add [SessionTimeOut] attribute on each controller.
[SessionTimeOut]
public class ControllerName : Controller
{
You should add Statup.cs file.
1. Add Statup Class your project from new item lists.
2. Add following line in ConfigureService.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options => options.EnableEndpointRouting =
false).SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
services.AddAuthorization();
services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
// we do this because we trust the network
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
});
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(x =>
{
x.Cookie.Name = "WriteSomeThings";
x.Cookie.SecurePolicy = CookieSecurePolicy.Always;
x.Cookie.SameSite = SameSiteMode.Strict;
x.Cookie.HttpOnly = true;
x.Cookie.IsEssential = true;
x.SlidingExpiration = true;
x.ExpireTimeSpan = TimeSpan.FromHours(8);//For Auto Logout
x.LoginPath = "/User/LogOn";
x.LogoutPath = "/User/LogOff";
x.AccessDeniedPath = "/Home/AccessDenied";
});
}
x.ExpireTimeSpan = TimeSpan.FromHours(8) => This line allow us to logout automatically after 8 hours.
If you need full user management check this video
https://youtu.be/912q3TEF25U
Software development template with role-based user management using ASP.NET MVC 5. Try it for free

Update: Passing HttpContext and IPrincipal to Controler in ReSharper unit test

I'm attempting to run a couple basic unit tests on an ASP MVC controller, however at one point the controller needs to examine the IPrincipal User object like so:
ViewBag.Level = Security.GetLevel(User);
Once the unit test enters the Create controller method, however, the above line throws a NullReferenceException as the User object is null.
Any ideas on how to set an IPrincipal for a unit test session?
Here's the test as I have it written right now. I attempted to access the User object and simply set it before the test goes into the Create method, however the intellisense isn't picking it up.
[Test]
public void a06_CloneFromDatabase()
{
using (AgentResources db = new AgentResources())
{
var master = (from a in db.AgentTransmission
where a.RecordStatus.Equals("C")
select a).FirstOrDefault();
var result = _controlAgtTran.Create(master.ID, null, string.Empty, string.Empty, string.Empty, string.Empty, false) as ViewResult;
var model = result.Model as AgentTransmission;
Assert.AreEqual(model.ReferenceType, master.ReferenceType);
}
}
EDIT
Following the post mentioned in the comments below I found a method to create a HttpContext session and apply and IPrincipal to that session. This works fine until the unit test moves into the controller where the HttpContext and IPrincipal User objects are all, once again, null.
Since it seems the instance of the controller I'm using has it's HttpContext property as read only (and the IPrincipal User property as well) does anyone know of a way to pass the HttpContext being used in the unit test inside the controller being tested? Also, if this is not possible, what is a usable method for testing RouteValues using ReSharper's unit tests?
[SetUp]
public void SetUp()
{
_controlAgtTran = new AgentTransmissionController();
/****Set up Current HTTP Context to pass Security.cs checks*****/
//Set up the HTTP Request
var httpRequest = new HttpRequest("", "http://localhost:2574/", "");
//Set up the HTTP Response
var httpResponse = new HttpResponse(new StringWriter());
//Set up the HTTP Context
var httpContext = new HttpContext(httpRequest, httpResponse);
var sessionContainer = new HttpSessionStateContainer("NEAROD",
new SessionStateItemCollection(),
new HttpStaticObjectsCollection(),
100,
true,
HttpCookieMode.AutoDetect,
SessionStateMode.InProc,
false);
httpContext.Items["AspSession"] =
typeof (HttpSessionState)
.GetConstructor(
BindingFlags.NonPublic | BindingFlags.Instance,
null,
CallingConventions.Standard,
new[] {typeof (HttpSessionStateContainer)},
null)
.Invoke(new object[] {sessionContainer});
//Assign the context
HttpContext.Current = httpContext;
}
[Test]
public void a01_IncompleteRecordGoesToEdit()
{
AgentTransmission agtTran = new AgentTransmission();
agtTran.ReferenceNumber = 95820518787;
agtTran.ReferenceType = "S";
agtTran.EffectiveDate = DateTime.Now;
agtTran.RelationshipEffDate = DateTime.Now;
agtTran.RecordStatus = "N";
agtTran.CreatedDate = DateTime.Now;
agtTran.CreatedOperator = "xTest1";
agtTran.FirstName = "Unit";
agtTran.LastName = "Test";
agtTran.ExtRepType = "EXTREPID";
agtTran.JIT = true;
agtTran.SendToDRM = true;
agtTran.SendToNMF = true;
agtTran.WelcomeLetter = true;
agtTran.OverrideRegionInd = false;
//set IPrincipal
string[] roles = {"LCO"};
IPrincipal principal = new GenericPrincipal(new GenericIdentity("SYMETRA\\NEAROD"), roles);
HttpContext.Current.User = principal;
IPrincipal user = HttpContext.Current.User;
Assert.AreEqual(user, principal); //This passes
Assert.AreEqual(principal, _controlAgtTran.User); //this fails
var result = (RedirectToRouteResult)_controlAgtTran.Create(agtTran); //this crashes
//Tests aren't run
Assert.IsNotNull(result);
Assert.AreEqual(3, result.RouteValues.Count);
Assert.AreEqual("AgentTransmission", result.RouteValues["controller"]);
Assert.AreEqual("Edit", result.RouteValues["action"]);
}
Following a similar solution mentioned in this post I added the following to the end of the SetUp() method.
var controllerCtx = new ControllerContext();
controllerCtx.HttpContext = new HttpContextWrapper(HttpContext.Current);
_controlAgtTran.ControllerContext = controllerCtx;
Wrapping the current HttpContext inside an HttpContextBase property (the inappropriately named controllerCtx.HttpContext) the test now has access to the User and HttpContext properties of the controller. These properties were previously read-only when using just the HttpContext.Current session and therefore always null.
FYI - this is my first time unit testing with these objects so that explanation may be less than 100% correct. Please feel free to comment below and I'll make any necessary changes.

How to check attribute of action from HttpRequest

I've followed this Prevent Forms authentication in order to try and handle redirecting from ajax gracefully. However I need to be able to determine if certain attributes are decorating the action that this call was made for as I only want to do this for some occasions. Can I get this information from the HttpRequest object that is accessible within this method?.
Essentially taking the part from the code above that I would like to manipulate:
public class SuppressFormsAuthenticationRedirectModule : IHttpModule {
private void OnPostReleaseRequestState(object source, EventArgs args) {
var context = (HttpApplication)source;
var response = context.Response;
var request = context.Request; // request is HttpRequest
if (response.StatusCode == 401 && request.Headers["X-Requested-With"] ==
"XMLHttpRequest") {
// TODO HERE: Check that the controller action contains a particular attribute
// and if so do not suppress redirect
SuppressAuthenticationRedirect(context.Context);
}
}
}
UPDATE:
It's probably worth noting that this code is held within a compiled DLL project that is then encorporated into a host MVC application (which we don't have access to). In that case I don't really have access to changing default implementations unless I can ensure it doesn't effect the rest of the controllers in the application.
I tried to use as much of the framework as possible, which is why I chose to expose the GetControllerType method from the DefaultControllerFactory. You'll notice that routeData contains the area, controller and action, so with a bit of reflection, you can bypass having to create a derived controller factory.
This is definitely not production ready. It is just a way to get the custom attributes from the requested action.
Edit: instead of setting the current controller factory, create a new DerivedControllerFactory
var httpApplication = (HttpApplication)sender;
var httpContext = new HttpContext(httpApplication.Request, new HttpResponse(new StringWriter()));
var routeData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(httpContext));
//var factory = ControllerBuilder.Current.GetControllerFactory() as DerivedControllerFactory;
var factory = new DerivedControllerFactory();
var controllerType = factory.GetControllerType(new RequestContext(new HttpContextWrapper(httpContext), routeData), routeData.Values["controller"].ToString());
var methodInfo = controllerType.GetMethod(routeData.Values["action"].ToString());
var attributes = methodInfo.GetCustomAttributes(true);
public class DerivedControllerFactory : DefaultControllerFactory
{
public new Type GetControllerType(RequestContext requestContext, string controllerName)
{
return base.GetControllerType(requestContext, controllerName);
}
}

Accessing User in Entity partial class via OnContextCreated()?

All of my tables have common audit fields: modifiedBy,modifiedDateTime, etc.
I would like to have these automatically set, and can set most of them with the following code:
partial class myEntities
{
partial void OnContextCreated()
{
this.SavingChanges += new EventHandler(Entities_SavingChanges);
}
private void Entities_SavingChanges(object sender, EventArgs e)
{
IEnumerable<ObjectStateEntry> objectStateEntries =
from ose
in this.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Modified)
where ose.Entity != null
select ose;
var auditDate = DateTime.Now;
var auditUser = System.Web.HttpContext.Current.User.Identity.Name;//I wish
foreach (ObjectStateEntry entry in objectStateEntries)
{
ReadOnlyCollection<FieldMetadata> fieldsMetaData = entry.CurrentValues.DataRecordInfo.FieldMetadata;
FieldMetadata modifiedField = fieldsMetaData.Where(f => f.FieldType.Name == "ModifiedBy").FirstOrDefault();
if (modifiedField.FieldType != null)
{
string fieldTypeName = modifiedField.FieldType.TypeUsage.EdmType.Name;
if (fieldTypeName == PrimitiveTypeKind.String.ToString())
{
entry.CurrentValues.SetString(modifiedField.Ordinal, auditUser);
}
}
}
}
}
The problem is that there doesn't appear to be any way to get access to the current user. The app is intranet only, using Windows auth.
Is there a way to either pass in a parameter, or get access to the HttpContext (which doesn't seem like it would be a good idea, but I'm stuck)? Is there a way to populate the EventArgs with information?
Check out the section where the poster has overridden the SaveChanges method (6th code box down on the page). This way you can pass in the UserID and perform your audit and not have to use an event handler.

ASP.NET MVC language change link

I have an ASP.NET MVC site that it's in two languages using Resources. To allow the server to present the site in the apropiate language (depending on the one that's configured in the user's browser) I put the following in my web.config:
<globalization culture="es-es" uiCulture="auto" />
How can I add a link to change the uiCulture? I want to store the selection in a cookie and if it's not present, then fall back to the browser configuration... Is it possible?
You may take a look at the following guide. It uses Session to store the current user language preference but the code could be very easily tweaked in order to use a cookie. The idea is that you will have a controller action:
public ActionResult ChangeCulture(string lang, string returnUrl)
{
var langCookie = new HttpCookie("lang", lang)
{
HttpOnly = true
};
Response.AppendCookie(langCookie);
return Redirect(returnUrl);
}
and then in Global.asax you could subscribe for the Application_AcquireRequestState event in order to set the current thread culture based on the value of the cookie:
protected void Application_AcquireRequestState(object sender, EventArgs e)
{
var langCookie = HttpContext.Current.Request.Cookies["lang"];
if (langCookie != null)
{
var ci = new CultureInfo(langCookie.Value);
//Checking first if there is no value in session
//and set default language
//this can happen for first user's request
if (ci == null)
{
//Sets default culture to english invariant
string langName = "en";
//Try to get values from Accept lang HTTP header
if (HttpContext.Current.Request.UserLanguages != null && HttpContext.Current.Request.UserLanguages.Length != 0)
{
//Gets accepted list
langName = HttpContext.Current.Request.UserLanguages[0].Substring(0, 2);
}
langCookie = new HttpCookie("lang", langName)
{
HttpOnly = true
};
HttpContext.Current.Response.AppendCookie(langCookie);
}
//Finally setting culture for each request
Thread.CurrentThread.CurrentUICulture = ci;
Thread.CurrentThread.CurrentCulture = ci;
//The line below creates issue when using default culture values for other
//cultures for ex: NumericSepratore.
//Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(ci.Name);
}
}
Now this being said using cookies and session to store current language is not SEO friendly. What I prefer doing when I need a localized application is to use a special route which will contain the language:
routes.MapRoute(
"Default",
"{lang}/{controller}/{action}/{id}",
new
{
lang = "en-US",
controller = "Home",
action = "Index",
id = UrlParameter.Optional
}
);
and then prefix all my urls with the language. This provides unique urls for different languages so that robots can properly index all content. Now all that's left is to modify the Application_AcquireRequestState method so that it uses the lang token of the url and based on its value set the proper Thread.CurrentThread.CurrentUICulture and Thread.CurrentThread.CurrentCulture.
And now when you wanted to change the language you would simply generate the proper link:
#Html.ActionLink("Page index en français", "index", new { lang = "fr-FR" })
An alternative and I feel it is more flexible
protected override void ExecuteCore()
{
if (RouteData.Values["lang"] != null && !string.IsNullOrWhiteSpace(RouteData.Values["lang"].ToString()))
{
SetCulture(RouteData.Values["lang"].ToString());
}
else
{
var cookie = HttpContext.Request.Cookies["myappculture"];
if (cookie != null)
{ SetCulture(cookie.Value); }
else
{ SetCulture(HttpContext.Request.UserLanguages[0]);}
}
base.ExecuteCore();
}
public ActionResult ChangeCulture(string lang, string returnUrl)
{
SetCulture(lang);
// Little house keeping
Regex re = new Regex("^/\\w{2,3}(-\\w{2})?");
returnUrl = re.Replace(returnUrl,"/" + lang.ToLower());
return Redirect(returnUrl);
}
private void SetCulture(string lang)
{
CultureInfo ci = new CultureInfo(lang);
System.Threading.Thread.CurrentThread.CurrentUICulture = ci;
System.Threading.Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(ci.Name);
// Force a valid culture in the URL
RouteData.Values["lang"] = lang;
// save the location into cookie
HttpCookie _cookie = new HttpCookie("myappculture", Thread.CurrentThread.CurrentUICulture.Name);
_cookie.Expires = DateTime.Now.AddYears(1);
HttpContext.Response.SetCookie(_cookie);
}
In the view
I kept the resource in a different project as follows
If you use the App_GloabalResources to store your resx language files, all you have to do is add a drop down which changes the current thread's UI Culture and this will automatically select the right resx language file to display.
App_GloabalResources is not the right place the resources when it comes to MVC programmering. See http://buildingwebapps.blogspot.no/2012/05/right-way-to-use-resource-files-for.html

Resources