Search code examples
javascriptc#ajaxasp.net-mvcurl.action

How to create consistent URLs with MVC .NET routing?


I am running ASP.Net MVC 5 in .NET Framework 4.8. I keep getting 404 error due to inconsistently generated URLs. For example, in my _PageNav.chstml partial that is included at the top of each page I the following to take the user back to the home page: @Url.Action("Index", new { controller = "Home" }). In the navigation bar this resolves to <a class="navbar-brand" href="/" /> and functions properly.

When I use the same @Url.Action("Index", new { controller = "Home" }) on the same page, but later in on a button, it resolves to this: <a id="doneButton" class="btn btn-secondary px-4" href="https://localhost:44337/smrt/">Done</a>

Because of this inconsistency I often have issues where AJAX JavaScript references to the controllers end up with missing controller references such as /create resulting in https://localhost:44337/create instead of https://localhost:44337/home/create or /home/create resulting in https://localhost:44337/home/home/create instead of https://localhost:44337/home/create

I do have also have some limitations because of security restrictions; for example I cannot have any JavaScript on the page itself so I can't write razor code in my .cshtml files that will result in JavaScript. I can only use JavaScript referenced in source files for the page.


Solution

  • It took a few different ideas, but I was able to solve my problem. I now get consistent URLs in just they way I need them, every time.

    First, I used this article How to Build Absolute Action URLs Using the UrlHelper Class, to create the follwoing class with an extension method based on the UrlHelper Class .

    using System.Web.Mvc;
    
    namespace MVCPages.Utilities
    {
        public static class UrlHelperExtenions
        {
            /// <summary>
            /// Generates a fully qualified URL to an action method by using
            /// the specified action name, controller name and route values.
            /// </summary>
            /// <param name="url">The URL helper.</param>
            /// <param name="actionName">The name of the action method.</param>
            /// <param name="controllerName">The name of the controller.</param>
            /// <param name="routeValues">The route values.</param>
            /// <returns>The absolute URL.</returns>        
            public static string AbsoluteAction(
                this UrlHelper url,
                string actionName,
                string controllerName,
                object routeValues = null
            )
            {
                var httpContext = url.RequestContext.HttpContext;
                string scheme = httpContext.Request.Url.Scheme;
    
                return url.Action(
                    actionName,
                    controllerName,
                    routeValues,
                    scheme
                );
            }
        }
    }
    

    This new method dynamically gives me the root of the website no matter where the site is deployed.

    I then call the new method extension in the views where I need URL consistency and pass the absolute root URL to the view using the ViewBag.

    public ActionResult Index()
    {
       string rootUrl = Url.AbsoluteAction("Index", "Smrt");
       ViewBag.RootUrl = rootUrl;
    }
    
    

    Since I am not allowed to use Razor to create JavaScript, I next use Razor to create a hidden HTML div tag that includes the root URL, <div id="rootUrl">https://rooturl.com/</div>

    Now, in JavaScript, I use the DOM to retrieve the URL from the HTML tag; then use ad that to my ajax calls to create a complete URL for the AJAX calls.

    window.onload = function () { 
       var rootUrl = document.getElementById("rootUrl").innerHTML;
       var ajaxUrl = rootUrl + ajaxCall;
    };