7

I have my project setup so that when my DD4T controllers throw an error they get redirected to an Error.aspx page. This page is setup in tridion and published. The redirect and loading of that page works just fine however in the event log for ever error I get this.

'System.Web.Mvc.HandleErrorInfo', but this dictionary requires a model item of type 'DD4T.ContentModel.IPage'.

I cannot get this to go away or figure out why it thinks it is getting the wrong model. When I look at the model on the page it says it is an IPage model type.

Any idea?

Not a lot of relevant code as it is pretty standard out of the box stuff:

<customErrors mode="RemoteOnly" defaultRedirect="~/Error.aspx" />

Page controller

public override ActionResult Page(string pageId)
    {
        pageId = UriHelper.ParseUrl(pageId);
        return base.Page(pageId);
    }

Component controller

public ActionResult Error(IComponentPresentation componentPresentation, [Bind(Prefix = QueryStringParameters.ClientId)] int? clientId)
    {
        ... stuff here

            ViewBag.Logo = Logo;
        }

        return this.View();
    } 
0

2 Answers 2

3

Previously we've done the following:

Turned off the global error filter:

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    //filters.Add(new HandleErrorAttribute());
}

Inside our main Tridion Page Controller, override the OnException method and used DD4T to go and retrieve our error page from Broker:

protected override void OnException(ExceptionContext filterContext)
{
    // Output a custom error page with correct status code
    // we have to do this because custom errors causes a 302 then 200 which is incorrect

    if (filterContext.HttpContext.IsCustomErrorEnabled)
    {
        int errCode = 500; //default
        var httpException = filterContext.Exception as HttpException;

        if (httpException != null)
        {
            errCode = httpException.GetHttpCode();
        }

        filterContext.ExceptionHandled = true;

        string errorPagePath;
        if (errCode == 404)
        {
            errorPagePath = WebConfiguration.Current.App404Path;
            Logger.Debug("TridionPageController Exception: ", filterContext.Exception);
        }
        else {
            errorPagePath = WebConfiguration.Current.AppErrorPath;
            Logger.ErrorFormat("TridionPageController Exception: {0}", filterContext.RequestContext.HttpContext.Request.Url.ToString());
            Logger.Error("TridionPageController Exception: ", filterContext.Exception);
        }

        // Get error page from Broker
        var error = base.GetModelForPage(errorPagePath);

        filterContext.Result = new ViewResult
        {
            ViewName = "~/Views/TridionPage/ContentPage.cshtml",
            MasterName = "~/Views/Shared/_Layout.cshtml",
            ViewData = new ViewDataDictionary { Model = error }
        };

        filterContext.HttpContext.Response.Clear();
        filterContext.HttpContext.Response.StatusCode = errCode;
        filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
    }
}
2
  • Worth noting that this will work by virtue of the fact that the controller's OnException method will always be executed before those in action filters, meaning that when the HandleError attribute gets hold of the filter context the exception has already been handled and the method returns immediately.
    – Ant P
    Commented Aug 1, 2014 at 13:07
  • Both answers were helpful but this approach aligned better with how need to address a custom error page. Thanks!
    – ToddB
    Commented Dec 1, 2014 at 15:54
7

I'll take a stab at explaining this.

DD4T's TridionControllerBase.Page method is decorated with the HandleError attribute. This is an inherited attribute, meaning it's also applied to your override.

Now, the HandleErrorAttribute (for which you can see the source here) replaces the view model in ViewData with its own HandleErrorInfo and renders out (by default) a view called Error.

It sounds like what's happening is that your Error page view is being picked up at this point and the attribute is passing the HandleErrorInfo to it, at which point the exception is thrown because your view expects an IPage instead. This second exception is then picked up by your global custom error handling and you're redirected to the page you actually want to be redirected to.

If you want to prevent the exception from being thrown, I think your only real option (beyond reworking your own error handling) is to extract the code from the out-of-the-box DD4T Page method (from here) and re-implement it yourself without including the HandleError attribute.

The simplest way would be to hide the existing method. Something along the lines of:

public new ActionResult Page(string pageId)
{
    pageId = UriHelper.ParseUrl(pageId);

    IPage model = GetModelForPage(pageId);
    if (model == null) { throw new HttpException(404, "Page cannot be found"); }
    ViewBag.Title = model.Title;
    ViewBag.Renderer = ComponentPresentationRenderer;
    return GetView(model);
}

Also, it's worth noting that you should have a static fallback error page for 500 errors - what happens if the exception is caused by a database connectivity problem and your error page has to be retrieved from the database?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.