0

I found a nice answer on creating breadcrumbs for this project here:

How can dynamic breadcrumbs be achieved with ASP.net MVC?

The modified code that I am using is here:

public static string BuildBreadcrumbNavigation(this HtmlHelper helper)
{
    var result = string.Empty;
    var controllerName = helper.ViewContext.RouteData.Values["controller"].ToString();
    if ((controllerName != "Home") && (controllerName != "Account"))
    {
        var htmlLink = helper.ActionLink(
            linkText: "Home",
            actionName: "/",
            controllerName: "Home").ToHtmlString();

        var sb = new StringBuilder($"<ol class='breadcrumb'><li>{htmlLink}</li>");

        var controllerLink = helper.ActionLink(
            linkText: controllerName,
            actionName: "/",
            controllerName: controllerName);

        sb.Append($"<li>{controllerLink}</li>");

        var actionName = helper.ViewContext.RouteData.Values["action"].ToString();

        var actionLink = helper.ActionLink(
            linkText: actionName,
            actionName: actionName,
            controllerName: controllerName);

        sb.Append($"<li>{actionLink}</li>");

        result = sb.Append("</ol>").ToString();
    }
    return result;
}

The webpage I want to implement it on has a navigation menu:

  • Services
    • Parts
    • Bulletins
    • Publications
    • Warranty
  • Sales
    • Buyers
    • Image Resources
    • Videos

For something like Home > Services > Parts, there is a controller called ServiceController.cs and a model called PartsInformation.cs.

The links for Home and Parts work fine, but there is nothing to display for the intermediate Services because it is a menu item only. Clicking Services attempts to redirect here:

https://localhost:44383/Services/

404

What should be done here?

Should I remove the link for Services and leave the text, or should I have Services route to Home?

I would like to route the root navigation menu items back to Home, but I don't understand enough about this ActionLink.

  • I would make a custom attribute in which to override the OnActionExecuting method and replace the route value with whatever you need. – Darkk L Dec 04 '22 at 16:37

2 Answers2

1

https://localhost:44383/Services/

It is trying to redirect to default action of ServiceController, which is typically named Index.

I think you have a few options (depending on what you want):

  1. Redirect to /Home:

    public class ServiceController : Controller
    {
        // assuming you want to redirect to HomeController's default action:
        public IActionResult Index() => Redirect("/Home");
    
        // or if you know HomeController's default action name,
        // you can use more idiomatic syntax:
        //
        // public IActionResult Index() => RedirectToAction("Index", "Home");
    }
    
  2. Utilize this space by adding an action Index page under Services (with list of services).

  3. Render Services in breadcrumb as text (instead of link):

    if (controllerName == "Services")
    {
        sb.Append($"<li>{controllerName}</li>");
    }
    else
    {
        var controllerLink = helper.ActionLink(
            linkText: controllerName,
            actionName: "/",
            controllerName: controllerName);
    
        sb.Append($"<li>{controllerLink}</li>");
    }
    

(I'd personally choose option #2 if it's my decision)

vulcan raven
  • 32,612
  • 11
  • 57
  • 93
  • Let me think on this. There are hundreds of links in the Navigation Menu, not just the 5 or 6 that I listed. I'd rather not have to go in and edit each of those hundreds of controllers because they don't happen to have method matching `public ActionResult Index()`. –  Dec 06 '22 at 17:45
  • You could use reflection in a static constructor of class containing `BuildBreadcrumbNavigation()` method to construct a map of all available controllers and their corresponding actions in your app; as in `Dictionary>`: https://stackoverflow.com/a/30969888/863980. Reflection is expensive, but this way it will execute once per application run. Then replace `if (controllerName == "Services")` condition with `if (!s_controllerActionMap.ContainsKey(controllerName) || !s_controllerActionMap[controllerName].Contains(actionName)) { /* render text instead of link */ }`. – vulcan raven Dec 12 '22 at 19:20
0

Here is what I have done until someone posts a better solution:

In the file Global.asax.cs, I added the Application_Error method to redirect when a controller is not found:

protected void Application_Error()
{
    var err = Server.GetLastError();
    if (err.GetType() == typeof(InvalidOperationException))
    {
        if (err.Message.IndexOf("The view 'Index' or its master was not found") == 0)
        {
            if (-1 < err.StackTrace.IndexOf("System.Web.Mvc.ViewResult.FindView(ControllerContext context)"))
            {
                // this controller does not have a view
                var request = HttpContext.Current.Request;
                var homeLink = $"{request.Url.Scheme}://{request.Url.Authority}{request.ApplicationPath.TrimEnd('/')}/";
                Response.Redirect(homeLink);
            }
        }
    }
}

It works, but it just doesn't seem like the way ASP.NET MVC should handle the case of links going to controllers that do not exist.