How do you setup SessionStateAttribute as a global filter in MVC3?
In my Global.asax I have this in the RegisterGlobalFilters method.
filters.Add(new SessionStateAttribute(SessionStateBehavior.Disabled));
And in my home controller I have this.
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
Session["Blend"] = "Will it blend?";
return View();
}
public ActionResult About()
{
return View();
}
}
But for some reason it still lets me use the Session. However if I decorate the HomeController class itself with the attribute, I get an error on the line utilizing the Session about a Object reference being null, which I'm guessing is intended if the Session is never created?
I am starting to wonder if there is something wrong with my project. I've been getting little problems like this one with standard behavior that are supposed to just work.
Anyone else had problems with things like this?
SessionStateAttribute is not an action filter, so you cannot add it as a global action filter. It's a special attribute which allows you to decorate your controllers with and have a more fine grained control over the session mode per controller.
To disable the session globally for the entire application put the following in your web.config:
<sessionState mode="Off" />
Related
I am trying to use the Authorize class in my ASP MVC3 app. Unfortunately due to business rules I need to pull the Roles from our web.config, however this is throwing the following exception:
An attribute must be a constant expression, typeof or array creation expression of an attribute parameter type
Here is the code I'm referencing.
[Authorize(Roles = ConfigurationManager.AppSettings.Get("user"))]
public class AdminController : Controller
{
Here is the user section of my web.config
<add key="user" value="SA\\Application.MortalityConcentrationRA.Dev.Users" />
Try creating a custom authorize attribute like this:
public class MyAuthorizeAttribute : AuthorizeAttribute
{
public MyAuthorizeAttribute()
{
this.Roles = ConfigurationManager.AppSettings["user"];
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
return base.AuthorizeCore(httpContext);
}
}
And using it in your controller like this:
[MyAuthorize]
public class HomeController : Controller
{
//code here
}
I'm afraid you'll need a custom authorize attribute. I looked around a bit and didn't find any other possible solution. The current attribute is requiring a constant, and I just do not know of a way to have your config.AppSettings[] ever be a constant value (it's pretty much by definition not a constant).
Have a look at this SO post that explains pretty much exactly what you need to do. You question is almost a duplicate of this one (which is good for you, there's already an answer).
I am developing an ASP.Net MVC 3 Web Application. I need to have my website secured with an SSL certificate, however, I only want this used when the application is on my live server, NOT on my test server.
Therefore, I setup an AppSetting in my Web Config like so
<appSettings>
<add key="SSL" value="false" />
</appSettings>
Then in my Account Controller I get this value (either True or False) and using the value, decide whether or not to set the RequiresHttps attribute on my LogOn Action. I would like to do something like so
public class AccountController : Controller
{
public string SSL = System.Configuration.ConfigurationManager.AppSettings["SSL"];
if (SSL.Equals("true"))
{
[RequireHttps]
}
public ActionResult LogOn()
{
return View();
}
}
But I know I can't put my IF statement where it currently is, however, hopefully you get the idea of what I am trying to achieve.
Does anyone have any suggestions as to how I can implement my idea?
Thanks.
Subclass the RequireHttpAttribute (note this code is changed from my original answer - this new version will be more efficient):
public class RequireHttpsIfEnabledAttribute : RequireHttpsAttribute
{
//this setting can't be changed without a recycle, so get it once and cache it.
private static readonly Lazy<bool> HttpsRequired = new Lazy<bool>(() => {
//if the AppSettings["SSL"] returns null you raise an exception if you do a
//.Equals on it - so do it on the constant instead. And make sure it's case
//insensitive!
return "true".Equals(System.Configuration.ConfigurationManager.AppSettings["SSL"],
StringComparison.OrdinalIgnoreCase);
});
public override void OnAuthorization(AuthorizationContext filterContext)
{
//calling the base will fire the HTTPS check. Not calling it will allow
//non-SSL requests through
if (HttpsRequired.Value)
base.OnAuthorization(filterContext);
}
}
Now you just decorate your controllers/actions as before - but with your new attribute:
[RequireHttpsIfEnabled]
public class AccountController : Controller
{
//....
}
I am trying to implement a very basic login scheme for my MVC3 site. If I understand correctly, instead of adding the [Authorize] markup to each of my controller classes, I should be able to simply implement a global setting. To accomplish this, I have added the following into global.asax:
protected void Application_Start()
{
RegisterGlobalFilters(GlobalFilters.Filters);
}
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new AuthorizeAttribute());
}
and in my webconfig, I added:
<authentication mode="Forms">
<forms loginUrl="~/Account/LogOn" timeout="2880" />
</authentication>
The result is that the resulting page is totally blank. Looking at the url, it seems that mvc is redirecting to my login route as expected except the page empty. If I comment out the code in global.asax and just place the [Authorize] markup directly in each contoller, it works as expected.
As a workaround, I have implemented what I have read the MVC2 best practice to be, which was to create a BaseController:Controller class, add the [Authorize] markup to it, and then change the inherentences of all of my controllers to inheret from BaseController instead of Controller.
That seems to work well enough for now.
But why isn't the global.asax implementation working?
Let's see what's happening here:
You are navigating to /
Your global authorize attribute kicks in and since the user is not authenticated he is redirected to ~/Account/LogOn (as instructed in your web.config file) for authentication
Your global authorize attribute kicks in and since the user is not authenticated he is redirected to ~/Account/LogOn (as instructed in your web.config file) for authentication
Same as 3.
Same as 4.
...
I think you get the point. The LogOn action should be excluded from authentication otherwise the user can never get a chance to login to your web site.
Since you have applied the Authorize attribute globally this cannot be done. One possible way is to write a custom AuthorizeAttribute that will be applied globally and which will exclude this action from authentication.
So you could write a marker attribute:
public class AllowAnonymousAttribute : Attribute
{
}
and a global custom authorize attribute:
public class MyAuthorizeAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
var exclude = ((AllowAnonymousAttribute[])filterContext.ActionDescriptor.GetCustomAttributes(typeof(AllowAnonymousAttribute), false)).Any();
if (!exclude)
{
base.OnAuthorization(filterContext);
}
}
}
that will be registered:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new MyAuthorizeAttribute());
}
Now all that's left for you is to decorate the controller actions that you want to be excluded from authentication with our marker attribute:
public class AccountController : Controller
{
[AllowAnonymous]
public ActionResult LogOn()
{
return View();
}
[AllowAnonymous]
[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
...
}
}
Why does T4MVC uses virtual for controller methods? Changing a
public ActionResult Details (string Id)
to:
public virtual ActionResult Details (string Id)
I have already seen other questions about T4MVC but didn't understand why.
Usually if a framework/library needs virtual methods (see also Nhibernate) it means somewhere/sometime your methods will be overridden.
So T4MVC marks your action methods as virtual because it's overrides them.
Lets take a simple controller:
public partial class HomeController : Controller
{
public virtual ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
return View();
}
}
If you go to the generated HomeController.generated.cs under the T4MVC.tt you will find a generated class which inherits from your controller and overrides your action method:
[GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
public class T4MVC_HomeController: MvcApplication8.Controllers.HomeController {
public T4MVC_HomeController() : base(Dummy.Instance) { }
public override System.Web.Mvc.ActionResult Index() {
var callInfo = new T4MVC_ActionResult(Area, Name, ActionNames.Index);
return callInfo;
}
}
I haven't used T4MVC so I don't know why and for what purpose T4MVC creates this generated class.
The only benefit I can see for making them virtual is to allow the developer to 'Go to Implementation/Definition' where The T4MVC helpers are used. This works because the Controller's type on the static Helper 'MVC' is the base controller type.
public static partial class MVC
{
public static HomeController Home = new T4MVC_HomeController();
}
So in the following snippet, Go to Definition on the Action Name will go to Base Implementation:
#Url.Action(MVC.Home.Index())
+1 David Ebbo for such an intuitive feature. I was mind blown when I realized this!
PS: this does not work for the parameterless actions added via the partial function, instead they navigate to the generated code, unfortunately.
How to display a default page like Maintenance page for all the views if a web.config key is set to true?
If the key is false, then show the regular views.
Plz note, I don't want to repeat the code in each controller and am looking for a common place, like _ViewStart or _Layout page where this can be defined.
Thanks.
You could create your own ActionFilterAttribute and base Controller, which gives you access to OnActionExecuting. You could then test if the Web.Config value is set (perhaps loading it into an Application variable when you first start your Web app) and if it is set, setting the Controller and Action attributes to your maintenance page. Then all of your controllers, instead of inheriting from the standard Controller would inherit from your controller instead, except for the Maintenance controller which could still inherit from the normal Controller.
For instance:
[RedirectToMaintenancePage]
public class MyController : Controller
{
}
And for a typical controller:
public class SampleController : MyController
{
... Your actions ...
}
Then create a class called RedirectToMaintenancePageAttribute.cs:
public class RedirectToMaintenancePageAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
If Web.config says to go to Maintenance page then...
{
filterContext.Result = RedirectToRouteResult("route name") // Or some other redirect
return;
}
// else
base.OnActionExecuting(filterContext);
}
}
This is all off the top of my head, but I think it should work, and if it doesn't hopefully it'll give you some ideas.