Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET MVC 3 Custom RouteBase and OutputCache

I have a problem with my custom RouteBase implementation and [OutputCache]. We have a CMS in which urls are mapped to certain content pages. Each type of content page is handled by a different controller (and different views). The urls are completely free and we want different controllers, so a "catchall" route is not usable. So we build a custom RouteBase implementation that calls the database to find all the urls. The database knows which Controller and Action to use (based on the content page type).

This works just great.

However, combining this with the [OutputCache] attribute, output caching doesn't work (the page still works). We made sure [OutputCache] works on our "normal" routes.

It is very difficult to debug outputcaching, the attribute is there we us it, it doesn't work... Ideas how to approach this would be very welcome, as would the right answer!

The controller looks like this:

public class TextPageController : BaseController
{
  private readonly ITextPageController textPageController;

  public TextPageController(ITextPageController textPageController)
  {
    this.textPageController = textPageController;
  }

  [OutputCache(Duration = 300)]
  public ActionResult TextPage(string pageid)
  {
    var model = textPageController.GetPage(pageid);
    return View(model);
  }
}

The custom route looks like this:

public class CmsPageRoute : RouteBase
{
  private IRouteService _routeService;
  private Dictionary<string, RouteData> _urlsToRouteData;

  public CmsPageRoute(IRouteService routeService)
  {
    this._routeService = routeService;
    this.SetCmsRoutes();
  }

  public void SetCmsRoutes()
  {
    var urlsToRouteData = new Dictionary<string, RouteData>();
    foreach (var route in this._routeService.GetRoutes()) // gets RouteData for CMS pages from database
    {
      urlsToRouteData.Add(route.Url, PrepareRouteData(route));
    }
    Interlocked.Exchange(ref _urlsToRouteData, urlsToRouteData);
  }

  public override RouteData GetRouteData(System.Web.HttpContextBase httpContext)
  {
    RouteData routeData;
    if (_urlsToRouteData.TryGetValue(httpContext.Request.Path, out routeData))
      return routeData;
    else
      return null;
  }

  public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
  {
    return null;
  }

  private RouteData PrepareRouteData(ContentRouteData contentRoute)
  {
    var routeData = new RouteData(this, new MvcRouteHandler());

    routeData.Values.Add("controller", contentRoute.Controller);
    routeData.Values.Add("action", contentRoute.Action);
    routeData.Values.Add("area", contentRoute.Area);
    routeData.Values.Add("pageid", contentRoute.Constraints["pageid"]); // variable for identifying page id in controller method

    routeData.DataTokens.Add("Namespaces", new[] { contentRoute.Namespace });
    routeData.DataTokens.Add("area", contentRoute.Area);

    return routeData;
  }

  // routes get periodically updated
  public void UpdateRoutes()
  {
    SetCmsRoutes();
  }
}

Thank you for reading until the end!

like image 421
Jaap Avatar asked Sep 14 '12 11:09

Jaap


1 Answers

In the end we tracked it down to a call to

 ... data-role="@this.FirstVisit" ...

in our _Layout.cshtml

This called a property on our custom view page that in turn called a service which always set a cookie. (Yikes setting cookies in services!, we know!)

Had it not been Friday and at the end of the sprint we might have noticed the cache busting Cache-Control: no-cache="Set-Cookie": Http Header.

I still don't understand why this only busted the cache for our custom RouteBase implementation and not all pages. All pages use the same _Layout.cshtml.

like image 183
Martijn Laarman Avatar answered Oct 08 '22 08:10

Martijn Laarman