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.
Related
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
{
//....
}
For a couple of my controllers I have a base class which implements the common actions.
e.g.
- BaseAccountController has a login action
- PublicAccountController inherits from BaseAccountController and implements further actions
- CorporateAccountController inherits from BaseAccountController and implements further actions
This gives the following routes:
- BaseAccount/Login
- PublicAccount/Login
- CorporateAccount/Login
What is the standard way to remove the route BaseAccount/Login as this is not desired. I'm hoping for an attribute on the controller :)
you can use the following custom attribute for your Base controllers:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
public class ClosedAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.Result = new HttpStatusCodeResult(404);
}
}
You'll receive 404 for your Base controllers routes - BaseAccount/Login. But please be sure that Inherited property is false (as in example).
Example of using:
[Closed]
public class BaseAccountController : Controller
{
...
}
you can read more (if needed) about custom action filters here - http://www.asp.net/mvc/tutorials/older-versions/controllers-and-routing/understanding-action-filters-cs
If the BaseAccountController has only login action then you can mark it as abstract.
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.
When a view model is created you can populate the options (e.g. used in a dropdown list) into a setter property of the view model.
The problem is that when that view model is later passed as a parameter (by the framework!) into an action method, those property values has not become automagically
repopulated, so if you need to redisplay the form because of validation errors, you need to repopulate those options again.
One potential solution, which I am asking for specifically in this question, is how to make the MVC framework instantiate the view model with constructor injection, which would provide the view model constructor with an implementation of some kind of data access object (e.g. a repository) that can be used for retrieving the options when they are requested by the view (e.g. in the helper method "DropDownListFor") ?
I think the solution might have something to do with implementations of IModelBinderProvider or IModelBinder but after having experimented with these things from example code snippets here and there on the net, I am still looking for a completely working example, with downloadable executable code without any missing piece of how putting all things together.
If you are looking for some alternative discussion about how to populate a select list, e.g. with "Dependecy Lookup" instead of "Dependecy Injection" you may want to check out the following discussion:
Best way to populate SelectList for ViewModel on GET/POST
Best way to populate SelectList for ViewModel on GET/POST
Some days ago I wrote the following follow-up-question in that thread about the "Dependecy Injection" I am now looking for in this thread:
https://stackoverflow.com/a/8674525/310457
(which provides a code example about the problem I am looking for a solution of)
But instead of hoping that someone will find that old thread with a less specific title, I have created this new question with a more specific subject about what I am looking for.
And I will also provide a link from that thread into this new question for anyone that want to follow-up regarding this specific solution I am looking for.
I'm assuming you want to have your ViewModels automatically injected with something via their Constructor - for example some kind of configuration object that the View will use to determine what to show. I'm also assuming that this approach is causing a "No parameterless constructor defined for this object" error when MVC tries to automatically create and bind a model instance, from the arguments of your Controller Action. Let's also then assume that we will use a DI framework to inject the SiteConfig object into our Controllers automatically at runtime.
This means that the only problem we have to solve is how to get the injected object from our Controller into its Actions' ViewModels when they are automatically bound.
So let's define a base model for others to inherit from.
BaseViewModel
public class BaseViewModel
{
public ISiteConfig SiteConfig { get; set; }
public BaseViewModel(ISiteConfig siteConfig)
{
this.SiteConfig = siteConfig;
}
}
And now let's create a model that inherits from it.
IndexViewModel
public class IndexViewModel : BaseViewModel
{
public string SomeIndexProperty { get; set; }
public IndexViewModel (ISiteConfig siteConfig) : base(siteConfig) {}
}
And now let's define a Base Controller that our Controllers will inherit from.
BaseController
public abstract class BaseController : Controller
{
protected BaseController(ISiteConfig siteConfig)
{
_siteConfig = siteConfig;
}
private readonly ISiteConfig _siteConfig;
public ISiteConfig SiteConfig
{
get
{
return _siteConfig;
}
}
}
Now we define our actual controller.
HomeController
public HomeController: BaseController
{
public HomeController(ISiteConfig siteConfig): base(siteConfig) {}
}
Assuming we're using Ninject for DI, Ninject would be configured to automatically create the Controller and pass a concrete ISiteConfig object into its Constructor at runtime.
Now we add our Action to the Controller.
Index Action
public ActionResult Index(IndexViewModel model)
{
return View(model);
}
And so this is the point where without doing anything else, MVC will explode with a "Parameterless Constructor" error if you try to call the Index Action, because MVC can't find a ViewModel constructor that takes no arguments.
And so, the answer. We need to override the default ModelBinder.
BaseViewModelBinder
public class BaseViewModelBinder : DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
if (modelType == typeof(BaseViewModel) || modelType.IsSubclassOf(typeof(BaseViewModel)))
{
var baseControl = controllerContext.Controller as BaseController;
if (baseControl == null)
{
throw new Exception("The Controller must derive from BaseController");
}
var instance = Activator.CreateInstance(modelType, baseControl.SiteConfig);
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => instance, modelType);
return instance;
}
else
{
return base.CreateModel(controllerContext, bindingContext, modelType);
}
}
}
And we need to set this as the default model binder in global.asax.cs :
protected void Application_Start()
{
...
ModelBinders.Binders.DefaultBinder = new BaseViewModelBinder();
}
That's all. As you can see, when you view the Index Action now, MVC will use our custom model binder. It will realise that the IndexViewModel derives from BaseViewModel, and so will attempt to spin up an IndexViewModel instance using the ISiteConfig it can find in the Action's Controller (because the Controller derives from BaseController).
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" />