Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Minimal way to handle static content routes/controllers/views from data driven menus?

I have a ListItem class that is used to represent menu items in my application:

 public class ListItem : Entity
{
    public virtual List List { get; set; }
    public virtual ListItem ParentItem { get; set; }
    public virtual ICollection<ListItem> ChildItems { get; set; }

    public int SortOrder { get; set; }
    public string Text { get; set; }
    public string Controller { get; set; }
    public string Action { get; set; }
    public string Area { get; set; }
    public string Url { get; set; }
}

I use this data to construct the routes for the application, but I was wondering if there was a clean way to handle controller/views for static content? Basically any page that doesn't use any data but just views. Right now I have one controller called StaticContentController, which contains a unique action for each static page that returns the appropriate view like so:

public class StaticContentController : Controller
{

    public ActionResult Books()
    {
        return View("~/Views/Books/Index.cshtml");
    }

    public ActionResult BookCategories()
    {
        return View("~/Views/Books/Categories.cshtml");
    }

    public ActionResult BookCategoriesSearch()
    {
        return View("~/Views/Books/Categories/Search.cshtml");
    }
}

Is there some way I could minimize this so I don't have to have so many controllers/actions for static content? It seems like when creating my ListItem data I could set the Controller to a specific controller that handles static content, like I have done, but is there anyway to use one function to calculate what View to return? It seems like I still need separate actions otherwise I won't know what page the user was trying to get to.

The ListItem.Url contains the full URL path from the application root used in creating the route. The location of the View in the project would correspond to the URL location to keep the organization structure parallel.

Any suggestions? Thanks.

Edit: My Route registration looks like so:

public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
        routes.IgnoreRoute("Shared/{*pathInfo}");
        routes.MapRoute("Access Denied", "AccessDenied", new { controller = "Shared", action = "AccessDenied", area = "" });
        List<ListItem> listItems = EntityServiceFactory.GetService<ListItemService>().GetAllListItmes();
        foreach (ListItem item in listItems.Where(item => item.Text != null && item.Url != null && item.Controller != null).OrderBy(x => x.Url))
        {
            RouteTable.Routes.MapRoute(item.Text + listItems.FindIndex(x => x == item), item.Url.StartsWith("/") ? item.Url.Remove(0, 1) : item.Url, new { controller = item.Controller, action = item.Action ?? "index" });
        }

        routes.MapRoute(
            "Default", // Route name
            "{controller}/{action}/{id}", // URL with parameters
            new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
        );


    }
like image 447
SventoryMang Avatar asked Jan 25 '26 20:01

SventoryMang


1 Answers

You can use a single Action with one parameter (the View name) which will return all the static pages

public class StaticContentController : Controller
{
    public ActionResult Page(string viewName)
    {
        return View(viewName);
    }
}

You will also need to create a custom route for serving these views, for example:

routes.MapRoute(
    "StaticContent",                                      // Route name
    "page/{viewName}",                                    // URL with parameters
    new { controller = "StaticContent", action = "Page" } // Parameter defaults
);

I see in your example that you specify different folders for your views. This solution will force you to put all static views in the Views folder of the StaticContentController.

If you must have custom folder structure, then you can change the route to accept / by adding * to the {viewName} like this {*viewname}. Now you can use this route: /page/Books/Categories. In the viewName input parameter you will receive "Books/Categories" which you can then return it as you like: return View(string.Format("~/Views/{0}.cshtml", viewName));

UPDATE (Avoiding the page/ prefix)

The idea is to have a custom constraint to check whether or not a file exists. Every file that exists for a given URL will be treated as static page.

public class StaticPageConstraint : IRouteConstraint
{
    public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
    {
        string viewPath = httpContext.Server.MapPath(string.Format("~/Views/{0}.cshtml", values[parameterName]));

        return File.Exists(viewPath);
    }
}

Update the route:

routes.MapRoute(
    "StaticContent",                                       // Route name
    "{*viewName}",                                         // URL with parameters
    new { controller = "StaticContent", action = "Page" }, // Parameter defaults
    new { viewName = new StaticPageConstraint() }          // Custom route constraint
);

Update the action:

public ActionResult Page(string viewName)
{
    return View(string.Format("~/Views/{0}.cshtml", viewName));
}
like image 169
shizik Avatar answered Jan 28 '26 13:01

shizik



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!