MVC routing for CMS pages
Add your "catch all" route on top of "default" route and add a route constrain
to path like this:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Static page",
"{*path}",
new { controller = "Content", action = "StaticPage" }
new { path = new PathConstraint() });
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional });
}
}
PathConstraint
should derive from from IRouteConstraint
interface and can be something like this:
public class PathConstraint: IRouteConstraint
{
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
if (values[parameterName] != null)
{
var permalink = values[parameterName].ToString();
//gather all possible paths from database
//and check if permalink is any of them
//return true or false
return database.GetPAths().Any(p => p == permalink);
}
return false;
}
}
So if "path" is not one of your pages paths, PathConstrain will not be satisified and "Static page" route will be skiped and pass to next route.
CMS Routing in MVC
Instead of simply using explode()
to separate the segments of URL, you could use a set of regular expression pattern.
For example, this following patter would try to match at firsts action
, and, if action
exists them check if there is controller
set before it:
'/(:?(:?\/(?P<controller>[^\/\.,;?\n]+))?\/(?P<action>[^\/\.,;?\n]+))?/'
Most of PHP frameworks use different ways to generate such patterns, with simplified notations. This way you can set which parts for each pattern are mandatory and which optional. And it is also possible to provide fallback values for the optional parts.
That said ...
Before starting to make something so complicated as cms with framework, you might invest some additional time in researching OOP. I would recommend to at least watch lectures from Miško Hevery and Robert C. Martin. Just because you think, that you know how to write a class, does not mean, that you understands object oriented programming.
Update
I have listed few materials in this answer. You might find them useful,
Additionally here are two more lectures, that were not in answer above:
- Clean Code I: Arguments
- Clean Code III: Functions
Dynamic Routes from database for ASP.NET MVC CMS
You can use a constraint to decide whether to override the default routing logic.
public class CmsUrlConstraint : IRouteConstraint
{
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
var db = new MvcCMS.Models.MvcCMSContext();
if (values[parameterName] != null)
{
var permalink = values[parameterName].ToString();
return db.CMSPages.Any(p => p.Permalink == permalink);
}
return false;
}
}
use it in route definition like,
routes.MapRoute(
name: "CmsRoute",
url: "{*permalink}",
defaults: new {controller = "Page", action = "Index"},
constraints: new { permalink = new CmsUrlConstraint() }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Now if you have an 'Index' action in 'Page' Controller like,
public ActionResult Index(string permalink)
{
//load the content from db with permalink
//show the content with view
}
- all urls will be caught by the first route and be verified by the constraint.
- if the permalink exists in db the url will be handled by Index action in Page controller.
- if not the constraint will fail and the url will fallback to default route(i dont know if you have any other controllers in the project and how you will decide your 404 logic).
EDIT
To avoid re querying the cms page in the Index
action in Page
controller, one can use the HttpContext.Items
dictionary, like
in the constraint
var db = new MvcCMS.Models.MvcCMSContext();
if (values[parameterName] != null)
{
var permalink = values[parameterName].ToString();
var page = db.CMSPages.Where(p => p.Permalink == permalink).FirstOrDefault();
if(page != null)
{
HttpContext.Items["cmspage"] = page;
return true;
}
return false;
}
return false;
then in the action,
public ActionResult Index(string permalink)
{
var page = HttpContext.Items["cmspage"] as CMSPage;
//show the content with view
}
MVC routing with optional routes
Ok I have a solution, it works for me because 99% of requests need to map to the CMS route but in your case it may not if you have a lot of controllers you need to map to.
I was hoping to find an ideal solution for all but this is merely an ideal in my scenario ...
So assuming you have (like me) only a cms controller and a an accounts controller you can do this:
routes.MapRoute("Account", "Account/{action}", new { controller = "Account" });
routes.MapRoute(
"Default",
"{*path}",
new { controller = "CMS", action = "GetPage", path = string.Empty }
);
That way only urls starting with "Account" get caught by the first rule, and everything else falls through to the default route and gets handled by the cms controller.
I plan to simply add more routes as I add more controllers. It's not an ideal solution because it could mean i end up with a lot of route mappings in the long term but its a good enough solution to meet my needs.
Hope it helps someone else out there.
Piranha CMS routing issue with ASP.NET MVC
By looking at your config I can see that you have followed the guidelines for setting up Piranha CMS for an existing project by setting passiveMode to true. Let me clarify a bit what this parameter does.
Passive mode is used for applications where you only want to use Piranha CMS as a backend content store and not handle any routing. This means that this parameter effectively turns off all url's to permalinks in the system to not interfere with the existing routes of the application.
If you want to mix your existing application controllers with pages solely generated by Piranha CMS you have to set passiveMode to false which will make the routing for permalinks active again.
Once this is done you will be able to access your pages with or without prefixless permalinks.
Regards
Håkan
MVC4 MapRoute for CMS
You can use:
routes.MapRoute(
name: "Default",
url: "{*p}",
defaults: new { controller = "Home", action = "Index", p = UrlParameter.Optional }
);
The asterisk indicates that it's a catch-all route. Keep in mind that these routes are extremely greedy, make sure that this stays below any specific routes.
You could also add a route constraint to this route which can determine whether the page exists in the database or something. See this post for more info.
Routing a custom controller in Orchard CMS
Try this:
@using (Html.BeginForm("Send", "Email", new { area = "Your.Module" }, FormMethod.Post, new { id = "contactUsForm" }))
Adding the area is like an extra clause that ensures only your module is searched for a matching controller/action method pair.
Related Topics
Getting the Difference Between Two Time/Dates Using PHP
Check If Value Exists Before Inserting into MySQL Db in a PHP Script
What Does a Leading Zero Do for a PHP Int
How to Parse Fixed Width Column Text in PHP
Turkish Characters Are Not Displayed Correctly
Fatal Error: Call to Undefined Method MySQLi_Stmt::Get_Result()
Heredoc Returning Unexpected End
.Htaccess Mod_Rewrite > 500 Internal Server Error
MySQL Pdo Name-Value Prepared Statement Using Last Parameter Only
Add a Line Break in Woocommerce Product Titles
Corresponding Nested Ternary Operator in PHP
Scraping a Dynamically Loading Website with PHP Curl
How to Do a Migration in Laravel 5.5