docs: function getWebRouteHandler(opts) {
      var webRouteHandler = this.pancakes.webRouteHandler;
      var me = this;
  
      return function handleWebRoute(request, reply, urlOverride, returnCode) {
          var appName = request.app.name;
          var url = urlOverride || request.url.pathname;
          var routeInfo = null;
          var referrer = request && request.headers && request.headers.referer ? request.headers.referer : '';
          var routeHelper = me.pancakes.cook('routeHelper');
          // var newrelic = require('newrelic');
  
          Q.fcall(function () {
              routeInfo = webRouteHandler.getRouteInfo(appName, url, request.query, request.app.lang, request.user, referrer);
              // newrelic.setTransactionName(routeInfo.appName + '||' + routeInfo.lang + '||' + routeInfo.urlPattern);
  
              // if redirect URL exists, just redirect right away
              if (routeInfo.redirect) {
  
                  // make sure tokens in the redirect URL are replaced
                  var redirectUrl = routeInfo.redirect;
                  _.each(routeInfo.tokens, function (value, key) {
                      redirectUrl = redirectUrl.replace('{' + key + '}', value);
                  });
  
                  if (redirectUrl.indexOf('{') >= 0) {
                      //throw new Error('Redirect URL has token that was not replaced: ' + redirectUrl);
                      // we can't just throw errors in here
                      console.log('Bad redirect: ' + redirectUrl);
                      reply(new Error('So sorry, but there is an error here right now, and we are trying to fix it. Please try again later.'));
                      return new Q();
                  }
  
                  // do full url for all redirects
                  if (redirectUrl.indexOf('http') !== 0) {
                      redirectUrl = (routeHelper.getBaseUrl(routeInfo.appName) + '') + redirectUrl;
                  }
  
                  reply().redirect(redirectUrl).permanent(true);
                  return new Q();
              }
  
              // if the app is handling this request, don't do anything
              if (opts.preProcess && opts.preProcess(request, reply)) {
                  return new Q();
              }
  
              var callbacks = {
                  serverPreprocessing:    me.getServerPreProcessing(request, reply),
                  addToModel:             opts.addToModel,
                  pageCacheService:       opts.pageCacheService
              };
  
              return webRouteHandler.processWebRequest(routeInfo, callbacks)
                  .then(function (renderedPage) {
  
                      // if no rendered page, it means request was handled earlier somewhere (ex. serverPreprocess redirect)
                      if (!renderedPage) {
                          //console.log('No rendered page for ' + url);
                          return false;
                      }
  
                      var response = reply(renderedPage).code(returnCode || 200);
  
                      // add content type if in the route info
                      if (routeInfo.contentType) {
                          response.header('Content-Type', routeInfo.contentType);
                      }
  
                      // as long as the nocache attribute is NOT set, add cache headers (sorry for double negative :-))
                      if (!routeInfo.nocache) {
                          response.header('cache-control', 'public, max-age=60');
                      }
  
                      return true;
                  });
          })
              .catch(function (err) {
                  console.error(err);
                  reply(err);
              });
      };
  }