3

I'm trying to add some custom routing logic based on url's stored in a database for mvc. (CMS Like), I think its fairly basic, but I feel like i'm not really getting anywhere.

Basically a user may type url's such as:

www.somesite.com/categorya/categoryb/categoryf/someitem
www.somesite.com/about/someinfo

In the database these items are stored, along with the type they are, i.e. a normal page, or a product page.

Depending on this I then want to actually hit a different 'action' method, i.e. I would like the above to hit the methods:

PageController/Product
PageController/Normal

These actions then load the content for this page and display the same view (product view, or a normal view).

Using the normal way of routing won't work, since I could potentially have things like;

cata/producta
cata/catb/catc/catd/cate/catf/producta

Now i've been looking here : ASP.NET MVC custom routing for search

And trying to use this as a basis, but how do I actually 'change' my action method I want to hit within the InvokeActionMethod call?

Using MVC 3.0 btw.

Thanks for any help/suggestions

Final Solution:

Global.asax

routes.MapRoute(
                "Default",
                "{*path}",
                new { controller = "Page", action = "NotFound", path= "Home" }
            ).RouteHandler = new ApplicationRouteHandler();

Route Handlers

public class ApplicationRouteHandler : IRouteHandler
    {
        public IHttpHandler GetHttpHandler(RequestContext requestContext)
        {
            return new ApplicationHandler(requestContext);
        }
    }

    public class ApplicationHandler : MvcHandler, IRequiresSessionState
    {
        public ApplicationHandler(RequestContext requestContext)
            : base(requestContext)
        {

        }

        protected override IAsyncResult BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, object state)
        {
            var url = RequestContext.RouteData.Values["path"].ToString();
            var page = SomePageService.GetPageByUrl(url);

            if (page == null)
            {
                RequestContext.RouteData.Values["Action"] = "NotFound";
            }
            else
            {
                RequestContext.RouteData.Values["Action"] = page.Action;
                RequestContext.RouteData.Values["page"] = page;
            }

            return base.BeginProcessRequest(httpContext, callback, state);
        }
    }
Community
  • 1
  • 1
BenW
  • 1,312
  • 10
  • 18

1 Answers1

4

Maybe not an exact solution for your situation, but I've recently had to handle something similar so this might point you in the right direction.

What I did was setup a simple route in Global.asax with a catch-all parameter which calls a custom RouteHandler class.

// Custom MVC route
routes.MapRoute(
    "Custom",
    "{lang}/{*path}",
    new { controller = "Default", action = "Index" },
    new { lang = @"fr|en" }
).RouteHandler = new ApplicationRouteHandler();

ApplicationRouteHandler.cs :

public class ApplicationRouteHandler : IRouteHandler
{
    /// <summary>
    /// Provides the object that processes the request.
    /// </summary>
    /// <param name="requestContext">An object that encapsulates information about the request.</param>
    /// <returns>
    /// An object that processes the request.
    /// </returns>
    public IHttpHandler GetHttpHandler(RequestContext requestContext)
    {
        string path = requestContext.RouteData.Values["path"] as string;
    // attempt to retrieve controller and action for current path
        Page page = GetPageData(path);

    // Method that returns a 404 error
        if (page == null)
            return SetupErrorHandler(requestContext, "ApplicationRouteHandler");

    // Assign route values to current requestContext
        requestContext.RouteData.Values["controller"] = page.Controller;
        requestContext.RouteData.Values["action"] = page.Action;
        return new MvcHandler(requestContext);
    }
}

Obviously the way you retrieve the action and controller names from your database will probably be much different than mine, but this should give you an idea.

dom
  • 6,702
  • 3
  • 30
  • 35
  • Does exactly what I need, thanks! Knew it would be something fairly trivial just couldnt find any decent examples! I'll accept this as bounty assuming theres no 'nicer' way at end of today :) – BenW Aug 28 '12 at 08:00
  • Slight hiccup, in that to determine the 'action' I need, i need to use the session, which is currently set to null Basically the handler is hit before the session_start in the global.asax is called, don't suppose you had similar problems? – BenW Aug 28 '12 at 09:21
  • You're getting `null` from `requestContext.HttpContext.Session`? I believe the only way it can be null is if you have nothing stored in it. – dom Aug 28 '12 at 12:20
  • Session_Start is hit after the GetHttpHandler method, but think we just need to re-work the way we're currently using the session – BenW Aug 28 '12 at 12:41
  • You're right, I just assumed session was available at that point but it isn't. You could try implementing the solution in this question (haven't tried it, so I have no idea...) : http://stackoverflow.com/questions/8810801/session-is-null-in-routehandler – dom Aug 28 '12 at 12:54
  • Pretty sure I tried that, and the issue is changing the routedata.values there, has no effect, it still hits the 'default' controller/action method – BenW Aug 29 '12 at 08:36
  • Haha looks like I did something slightly wrong, i'll post my fully working solution above, thanks so much for your help dombenoit – BenW Aug 29 '12 at 09:01