Custom 404 page in EPi 7 MVC

Member since: 2010

I want to have a 404 page, that is an EPiServer page.

This is my web.config

    <httpErrors errorMode="Custom" existingResponse="Replace" defaultResponseMode="ExecuteURL">
      <remove statusCode="404" />
      <error statusCode="404" path="/404" responseMode="ExecuteURL" />
      <remove statusCode="500" />
      <error statusCode="500" path="/500.html" responseMode="ExecuteURL" />
    </httpErrors>

This is my Controller:

    public class NotFoundPageController : PageController<NotFoundPage>
    {
        public ActionResult Index(NotFoundPage currentPage)
        {
            var viewModel = new NotFoundPageViewModel();
            Mapper.Map(currentPage, viewModel);

            Response.StatusCode = 404;

            return View(viewModel);
        }
    }

    and finally, my Global.asax part:

        protected void Application_Error(object sender, EventArgs e)
        {
            var lastError = HttpContext.Current.Server.GetLastError();
            if (lastError != null)
            {
                var httpException = lastError as HttpException;
                if (httpException != null && httpException.GetHttpCode() == 404)
                {
                    RedirectTo404ErrorPage();
                }
            }
        }

        private static void RedirectTo404ErrorPage()
        {
            var startPage = ContentReference.StartPage.GetPage() as StartPage;
            if (startPage != null && startPage.ErrorPage != null)
            {
                var errorPage = startPage.ErrorPage.GetPage();
                if (errorPage != null)
                {
                    HttpContext.Current.Response.Redirect(errorPage.ToFriendlyURL());
                }
            }
        }

    

What I saw online is that MVC should have a different 404 error handling, such as this: http://stackoverflow.com/questions/619895/how-can-i-properly-handle-404-in-asp-net-mvc, however, it's related to non-CMS MVC.

My code gives me indefinite redirection and if I ommit Response.StatusCode = 404; than I don't get the 404 status code in the browser.

Has anyone got ahead of this?

 

#67838 Mar 13, 2013 14:46
  • Member since: 2003

    To avoid possible infinite redirection issues I'm using static html files for 404 and 500:

    <httpErrors errorMode="Custom" defaultResponseMode="File" existingResponse="Replace">
    <clear />
    <error statusCode="404" path="Static\404.html" />
    <error statusCode="500" path="Static\Error.html" />
    </httpErrors>

    #68170 Mar 18, 2013 10:50
  • I'm also looking for a solution to this problem. Of course I could use static html files as Mari suggests, but that's not really a "solution".

    So, anyone got a solution for this?

    #69756 Apr 04, 2013 17:18
  • Member since: 2010

    I have this in my config:

     <httpErrors errorMode="Custom" existingResponse="Replace">
          <clear/>
          <error statusCode="404" responseMode="ExecuteURL" prefixLanguageFilePath="en" path="/system/notfound/" />
          <error statusCode="500" responseMode="ExecuteURL" prefixLanguageFilePath="en" path="/system/error/" />
        </httpErrors>

        

    Then I just created the pages in the CMS as any other page (so, my path is /system/nofound/)

     

    #69774 Apr 05, 2013 13:14
  • Member since: 2010

    So, you don't have anything in Global.asax? Do you get the 404 status code for a non-existant page in Fiddler?

    #69776 Apr 05, 2013 13:36
  • Member since: 2010

    I added nothing to my Global.asax page.

    It wasn't initally returning a 404 status code, but I just added the following to the top of the templates .cshtml page

    Response.StatusCode = 404;

       

    I have a standard template, so just used logic on the page title in the CMS to dertmine if I should include the status code. 

    #69780 Apr 05, 2013 14:26
  • I thought I have tried Danny's solution yesterday, I think I'll have to give it one more try.

    Anyway, I think I've come up with another working solution. But I still need to test it a bit more, but so far it seems to be working fine. I get 404 status code, it shows information from a epi page, it doesn't redirect and it doesn't get stuck in a redirection loop.

    I'm gonna test it a bit more before I post it here, but in short:

    * Add a routing rule for the path "404"  in global.asax to a controller and a NotFound-action in that controller.
    * Add httpErrors in web.config with a 404 error rule to redirect to "/404" (as in Dannys solution)
    * Write logic in the NotFound-action to get a ContentReference to the epi 404-page and return it as a model to the view.

    #69781 Apr 05, 2013 14:35
  • Member since: 2010

    I don't have time to try this now, but I think if you just add

    Response.StatusCode = 404;

    to the cshtml, you would get a 404 for the page /404 and you need to get a 404 status code for the page that doesn't exist. If a page doesn't exist, you would want a search engine to get 404 for it and not 301 or 302 or 200.

    #69788 Edited, Apr 05, 2013 15:59
  • Member since: 2010

    You know, I think you're probably right thinking about it.

    I'll do some more testing on it, to be sure.

    :|

    #69789 Apr 05, 2013 16:01
  • I'm pretty confident my solution works so here is some sample code,

    In global.asax

            protected override void RegisterRoutes(System.Web.Routing.RouteCollection routes)
            {
    
                    // Route to handle 404's
                    routes.MapRoute(
                        "404",
                        "404",
                        new {controller = "DefaultPage", action = "NotFound"});
    
                base.RegisterRoutes(routes);
            }

        

    In web.config

        <httpErrors errorMode="Custom" existingResponse="Replace">
          <clear/>
          <error statusCode="404" responseMode="ExecuteURL" prefixLanguageFilePath="en" path="/404" />
        </httpErrors>

        

    In DefaultPageController.cs

            public ActionResult NotFound(SitePageData currentPage)
            {
                // Get StartPage
                var startPage = ContentReference.StartPage.GetPage<StartPage>();
    
                // Get ErrorPage
                var notFoundPage = startPage.NotFoundPage.GetPage<ErrorPage>();
    
                if (notFoundPage == null)
                    throw new NullReferenceException("No 404 page has been set on the StartPage");
    
                var model = CreateModel(notFoundPage);
    
                // Set RoutingConstants.NodeKey so EPi's extension method RequestContext.GetContentLink() will work
                ControllerContext.RouteData.DataTokens[RoutingConstants.NodeKey] = startPage.NotFoundPage;
    
                Response.StatusCode = 404;
                return View("~/Views/ErrorPage/Index.cshtml", model);
            }

     And finally add a property on the StartPage-model,

    public virtual PageReference NotFoundPage { get; set; }

           

    As I said I'm pretty sure this code works, but it's more a proof of concept and is not production ready code!

    EDIT:

    Some of the code is using extension methods and methods that's part of my solution, but I'll hope you'll get the idea by looking at the code. Feel free to let me know if anything is unclear. 

    #69797 Edited, Apr 05, 2013 19:17
  • Member since: 2013

    I tested Daniel's solution and it worked like a charm!

    I made my controller like this:

            public ActionResult NotFound(SitePageData currentPage)
            {
                StartPage startPage = (StartPage)DataFactory.Instance.GetPage(PageReference.StartPage);
                var errorPage = DataFactory.Instance.GetPage(startPage.NotFoundPage);
    
                // Set RoutingConstants.NodeKey so EPi's extension method RequestContext.GetContentLink() will work
                ControllerContext.RouteData.DataTokens[RoutingConstants.NodeKey] = startPage.NotFoundPage;
    
                Response.StatusCode = 404;
    
                return RedirectToAction("Index");
            }

    That will send me to my specified EPiServer page. For example if i choose my About page I will be sent to www.domain.com/About . This also works with ViewModels and such.

    #70613 Apr 24, 2013 12:44
First   1 2 3 4   Last