I need to retrieve the data from cookie in every request in ASP.NET MVC and store it in a global variable so that it'll be available throughout the application.
I've two questions here is there any event-handler in ASP.NET MVC where I can get the data from cookie in every request and what kind of global variable I can use to store this cookie value so it is available in all places?
You can use a filter to get the cookie in every request. Create for example a class MyFilter
public class MyFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
HttpCookie cookie = filterContext.HttpContext.Request.Cookies["myCookie"];
//do something with cookie.Value
if (cookie!=null) filterContext.HttpContext.Session["myCookieValue"] = cookie.Value;
// or put it in a static class dictionary ...
base.OnActionExecuting(filterContext);
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
}
}
Mark every controller class with [MyFilter] attribute.
This example takes the cookie and puts the value in the session so it's available in all the views and all the controllers. Else you can put the cookie value in a static class that contains a static Dictionary and use the session ID as key. There are many way to store the cookie value and access it in every part of the application.
You can use Attributes on each request or make a custom Controller and handle "OnActionExecuting" (override)
You could go old school and handle the onrequest event in the asax file. That way you could abstract the code out to an httpmodule if you need to reuse the approach in another app. The filters approach is probably better though.
Related
I recently asked this question and have successfully setup my custom authorize attribute.
But now I have hit another wall regarding api calls. I need these calls to also be authorized in the same fashion as the action calls. I understand that there is a difference between System.Web.Http and System.Web.Mvc Authorize attributes. So I have created a separate Api specific attribute that basically does the same thing. However, I am having trouble setting the User - principal and identity like i do in the original attribute.
My attributes simply check for some values in a cookie to authorize the request, once the attribute has read the cookie I was storing the decrypted cookie information within a custom principal/identity setup. In my Api call, when I go to retrieve this information from the identity my cast fails and i receive a null value.
This is how I store the information
Api
HttpContext.Current.User = new MyPrinciple(new MyIdentity(decCookie.Name, decCookie.UserData));
Action
filterContext.RequestContext.HttpContext.User = new MyPrinciple(new MyIdentity(decCookie.Name, decCookie.UserData));
How I retrieve the desired information I assumed would be the same
(User.principal.Identity as MyIdentity).MyData;
Questions
Do I really need to have 2 separate attributes
For the Api attribute how can I easily store the information for later use within the controller. Or basically can I not actually get/set the Identity this way for these calls?
EDIT #1
I found how to properly access my cookie value from my ApiController, I was simply missing a reference to System.Web >_<. So question #2 has been solved! but #1 still remains.
Web API and MVC have nothing in common (technically) - even when they look the same. You need two separate attributes.
You can only inherit in one class from in c#, and each authorizeattribute lives in its own namespace, so you can't do it in a single class.
You could hold it in a common namespace and then call a common class to do the lifting.
Possible solution (untested)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
namespace Common.Attributes
{
public class CustomAuthorize : System.Web.Mvc.AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
if (!HttpContext.Current.Request.IsAuthenticated || (HttpContext.Current.User as User) != null)
return;
filterContext.HttpContext.User = Authorize.ExtractIdentity(filterContext.HttpContext);
}
}
public class CustomHttpAuthorize : System.Web.Http.AuthorizeAttribute
{
public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
{
base.OnAuthorization(actionContext);
if (!HttpContext.Current.Request.IsAuthenticated || (HttpContext.Current.User as User) != null)
return;
System.Threading.Thread.CurrentPrincipal = Authorize.ExtractIdentity(filterContext.HttpContext);
}
}
}
public static class Authorize
{
public static IIdentity ExtractIdentity(HttpContext context)
{
// do your magic here
}
}
}
How can I ensure that information isn't returned from a webmethod or mvc action by just anyone calling it from Jquery/Ajax call/HTTP post get (you get the idea).
Previously I would have used session variables or the view state to do this on every call, is this still possible. Can anyone point me to any good demos around making these calls secure, I've seen a few but they are easy to spoof or work around.
Thanks!
You can use the AuthorizeAttribute as an action filter to filter access to your controllers. You can just add this attribute to the controllers you want to limit the access to. I think the msdn has a good example for this:
http://msdn.microsoft.com/en-us/library/dd381413(v=vs.90).aspx
You can also use Session in this case.
1. create a ActionFilterAttribute class named, e.g., LoginFilterAttribute.
public sealed class LoginFilterAttribute:ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
//check if the user is logged in
if (Session["UserSessionKey"]==null)
//redirect to login page or do nothing
else
//return data or do something else
}
}
2. in your action, put this attribute before the action method
[LoginFilter]
public ActionResult ActionNeedLogin()
{
return View();
}
or, register the attribute in global.asax to keep all action from anonymouse access.
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new MyHandleErrorAttribute());
filters.Add(new LoginFilterAttribute());
}
I've an ASP.NET MVC blog, in order to show the posts and comments dates in client timezone a cookie is used, the cookie contains the client timezone offset. When the server receives a request it will read the offset value from cookie and changes all the dates accordingly before sending to browser. My question how I can store the cookie in a global variable on every request so that it can be accessed by any where for date adjustment.
Generally, the more controller and action depend on values supplied from outside, the more unit testable and robust they become. I would do it this way
First, create model that holds settings for timezone
public class ClientTimeZoneSettings
{
public string TimeZoneName {get; set;} // or whatever
}
Then, create model binder. That Model binder will be used to extract values from cookie
public class ClientTimeZoneSettingsModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
if (controllerContext.RequestContext.HttpContext.Request.Cookies.AllKeys.Contains("timeZoneName"))
{
bindingContext.Model = new ClientTimeZoneSettings {TimeZoneName = controllerContext.RequestContext.HttpContext.Request.Cookies["timeZoneName"]; }
}
}
}
Register that model binder in Global.asax
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
ModelBinders.Binders.Add(typeof(ClientTimeZoneSettings), new ClientTimeZoneSettingsModelBinder());
}
And the main point. In all your actions that require those settings, you can directly use ClientTimeZoneSettings as a parameter
public ActionResult ShowComments(ClientTimeZoneSettings settings)
{
// use settings
}
UPDATE: Significantly simpler approach:
Install MvcFutures from nuget. It contains CookieValueProviderFactory that will automatically inspect cookies for values when model binding. To use it, simply add into ValueProviderFactories
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
ValueProviderFactories.Factories.Add(new CookieValueProviderFactory());
}
And then name your parameter accorting to cookie name
public ActionResult ShowComments(string timeZoneName)
{
// timeZoneName will contain your cookie value
return View();
}
You can use a session variable if you don't want to use the cookie every time
session["MyVarName"] = mycookievalue
then you can access the session every time needed.
You can also think of implementing e custom modelbinder so you can bind your session's value to a model. (for example a class UserSettingsModel)
I want to be able to globally access the logged-in user in (Controllers, HtmlHelpers and other helper classes) throughout the whole application without getting exceptions that User is null and with the ability to handle guests (not-logged-in users).
Should this be handled within my UsersRepository? If so, how to? or is there a better approach?
You can create custom identity and principal classes. Your custom identity class can extend whatever you are currently using and can then simply add the extra information you need.
Then in a global action filter, simply overwrite the current principal with your customized one. Then you can access it from anywhere like a normal identity, but if you need your additional information, you simply cast it to your custom identity class. Which will grant you access to your additional information.
You can write a custom action filter that is executed on every request (you register it as a global filter). This filter would load the user (from the user´s repository for example) and put it the http context for example or in the ViewData.
EDIT:
Ok, the code for the filter could look like this (in this case, it loads the user to the ViewData collection). I didn´t consider anonymous users here.
public class LoadUserToViewDataAttribute : ActionFilterAttribute
{
private IUserRepository _userRepository;
public LoadUserToViewDataAttribute(IUserRepository userRepository)
{
_userRepository = userRepository;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var controller = filterContext.Controller;
var userName = filterContext.HttpContext.User.Identity.Name;
var user = _repository.GetUser(userName);
controller.ViewData.Add("CurrentUser", user);
}
}
I have multi-tenant ASP.NET MVC application which utilizes subdomains to determine the current tenant. Whether or not the domain is valid is determined via database table lookup.
Where would be the best place to have a function that checks if the domain is in the database?If the subdomain is not in the database, it should redirect to the Index action in the Error controller.
Placing the check in the Application_BeginRequest method in the Global.asax file doesn't work because a never ending redirect results.
Where would be the best place to have a function that checks if the domain is in the database?If the subdomain is not in the database, it should redirect to the Index action in the Error controller.
Placing the check in the Application_BeginRequest method in the Global.asax file doesn't work because a never ending redirect results.
That's the right place, you just need to check the request Url is not already /Error.
You might already be doing so, but I'd like to add that it seems pretty static information that you should cache instead of hitting the database for each request.
u can subclass actionFilter attribute and override onactionExecuting method. in this method u can make any database checks and redirect the user appropriately
public class CustomActionFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if(DatabaseLookup)
{
return;
}
filterContext.Result = new RedirectResult("http://servername/Error");
}
}
now u can decorate ur action methods with this custom actionfilter attribute
[CustomActionFilter]
public ActionResult mymethod()
{
//action method goes here
}