How to set a Razor layout in MVC via an attribute

2020-02-26 10:48发布

问题:

I'd like to set a default Razor layout via code in either a base controller or an attribute. It was mentioned in the Docs that this is possible, but I can't figure out how it's done.

I know there's the masterPage parameter to the View method available, but I would like all views returned by a controller to have this value set automatically.

And no, I can't use _ViewStart for this since my views are going to be in various places (this is not a normal MVC site configuration).

Thanks

回答1:

I think you could just write an ActionFilter like...

public class YourCustomLayoutAttribute : ActionFilterAttribute, IResultFilter
{
      public override void OnResultExecuting(ResultExecutingContext filterContext)
      {
           var viewResult = filterContext.Result as ViewResult;
           if(viewResult != null)
           {
              // switch the layout
              // I assume Razor will follow convention and take the "MasterName" property and change the layout based on that.
              viewResult.MasterName = "CustomLayout";
           }
       }
}

I just wrote this code by the seat of my pants with no compiler so it probably won't compile but you probably get the idea. I think IResultFilter is the correct interface you want, it has methods that execute right before the view is rendered. If this is correct, you should be able to modify the MasterName for the view that is about to be rendered on the fly.

This would be the controller code usage.

[YourCustomLayout] // this should trigger your custom action result for all actions
public class MyController : Controller
{
   public ActionResult Index()
   {
      return View("Index", "MainLayout"); // even if you were to use the overload to set a master, the action result should override it as it executes later in the pipeline.
   }
}


回答2:

The easiest way I can think of doing this is by having your controllers derive from a custom base class that overrides the View method:

public class MyControllerBase : Controller {
    public override ViewResult View(string viewName, string masterName, object model) {
        if(String.IsNullOrEmpty(masterName)) {
            masterName = GetDefaultLayout();
        }
        base.View(viewName, masterName, model);
    }

    public virtual string GetDefaultLayout() {
        return // your default layout here
    }
}

In the code above you could explicitly set the masterName to some hardcoded value. Or your Controllers could override the method to provide a Controller-specific layout. Or you could read it from some attribute on the Controller, something similiar to:

masterName = GetType().GetCustomAttributes().
             OfType<MyCustomAttribute>().FirstOrDefault().DefaultLayoutPage;

Of course you'd have to create your MyCustomAttribute.