
Formulation of the problem
The ASP.NET MVC routing system eliminates the need for the developer to manually maintain the URLs used in the application with the help of routing tables and url patterns. Thus, the programmer removes the task of forming the URL manually. On the contrary, it has at its disposal a rich set of URL-helpers. It is wonderful! But everything changes when they arrive -
AJAX requests .
The problem is that the routing tables and the URL generation mechanism are part of the asp.net mvc server architecture and are not accessible from the Javascript files.
As a result, in the scripts you can often find such code:
var url="/items/get-items?typeid="+typeId+"&groupid="+groupId+"&skip="+skip+"&take="+take;
In addition to the fact that this code does not look very nice, its appearance in at least one place in your scripts violates the isolation of the information about the final form of the URL. Now, in case you need to change the URL structure and, for example, transfer parameters from the query string to the body of the URL, it will not be enough for you to edit the template. You will need to go through the scripts and make the necessary changes. This can be the cause of errors: somewhere changed, somewhere forgot, somewhere sealed.
')
Pre-server URL generation
Client scripts should be located in .js files, so we immediately discard the option with generating scripts when creating the View. One solution in this situation is to pre-generate the URL on the server and write it to the data- * attribute of the element, for example:
<div id="some_container" data-url="@Url.Action("Action","Controller",new {Id=10})"> ... </div>
Here we still rely on the ASP.NET MVC routing mechanism and get rid of manually generating a URL on the client, getting it from the attribute. In certain cases, this solution will be enough, but this does not completely solve the problem. Consider the situation. You are writing a javascript module, which is planned to be used in many parts of the application, and at the same time it will need to make ajax requests. In this case, I would not like to bring the URL information for requests somewhere outside this module. Therefore, the option of storing the URL in the attributes and passing it inside the module does not suit us. But we do not want to form it manually. What to do?
Library RouteJs
Ideally, I would like to have access to URL routes directly from JavaScript, but there is no built-in mechanism in ASP.NET MVC for this. However, you can add it by connecting a small library
RouteJs . It works with both WebForms and ASP.NET MVC (starting with version 2) and does not require the use of third-party JavaScript frameworks. You can add it to the project via
Nuget . In this case, you will only need to include in the page a link to the script:
<script src="@RouteJs.RouteJsHandler.HandlerUrl"></script>
As a result, you will get access to your routes directly from JavaScript using the Router object.
Examples
Default route
routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } );
What we get:
>Router.action('Home','Index') "/Home/Index" >Router.action('Home','Index',{id:1}) "/Home/Index/1" >Router.action('Home','Index',{type:1, group:2}) "/Home/Index?type=1&group=2"
And if you add the Area?
public override void RegisterArea(AreaRegistrationContext context) { context.MapRoute( "TestArea_default", "TestArea/{controller}/{action}/{id}", new { action = "Index", id = UrlParameter.Optional } ); }
Checking:
>Router.action('Home','Index',{area:'TestArea'}) "/TestArea/Home/Index"
Constraints
RouteJs can work with default regexp restrictions:
routes.MapRoute( name: "DefaultWithConstraint", url: "constr/{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }, constraints:new {action="^ConstrAction$"}
Check:
>Router.action('Home','Index') "/Home/Index" >Router.action('Home','ConstrAction') "/constr/Home/ConstrAction"
However, for obvious reasons, he will not be able to handle your custom IRouteConstaints. Moreover, in the current version, Router stops working if any of the routes uses custom Constraints. (An error occurs in JavaScript when accessing the object. To get around this, you should
[HideRoutesInJavaScript]
attribute to the problem controller. I think this bug will be fixed in the near future).
Caching
The generated script is sent along with the Expires header, which informs the browser that the script needs to be cacheted for 1 year. In this case, pay attention to the format of the link that is added to the page:
<script src="/routejs.axd/2512dbb48b9018a4c8f9eac5fb92f903800d94da/router.js"></script>
The link contains some hash, which is formed from the list of routes and changes with them. This ensures that there is a valid route table on the client when caching.
JsAction library
The
JsAction library offers a different approach to solving client-side routing problems. You can also install it via
Nuget . After that you need to add a link to the Layout (should be connected after jQuery):
@Html.JsActionScript()
Next, the Action that you plan to use for Ajax requests is marked with a special attribute
[JsAction]
:
[JsAction] [HttpGet] public JsonResult Do(int a, int b) { return Json(a + b, JsonRequestBehavior.AllowGet); }
After that you can call this Action from JavaScript by name:
$(function() { JsActions.Home.Do(1, 3); })
Thus, this library does more than just transfer route URLs to a client. It saves the developer from manually writing Ajax requests. At the same time there are a number of pleasant side effects. Imagine that the method that was available at the GET request needs to be replaced with POST. Your actions? You change the [HttpGet] attribute to [HttpPost] and then go look for a call to this method in the script to change the $ .Ajax call settings (if you use jQuery, of course). This library saves you from this need. From now on, these settings will be only in the attributes of the method. Processing the results of an ajax request is the same as with the usual $ .ajax () call:
$(document).ready(function () { JsActions.Home.Do(1,2).then(function(data){ alert(data); }); });
In case you use jQuery up to version 1.5, you can use a similar mechanism:
$(document).ready(function () { var ret = JsActions.Home.Do(1, 2, { success: function (data) { alert(data); } }); });
Intellisense
JsAction supports the Intellisense mechanism in Visual Studio. It works as follows. You add xml comments to your method:
At the same time in the project settings there should be a tick "XML documentation file":

After that, open Tools-> Library Package Manager-> Package Manager Console and enter the
JsAction-GenDoc command . As a result, the JsActions.vsdoc.js file will be generated in the Scripts folder. Now when writing scripts, Visual Studio can help you:


Caching
Unlike the previous library, caching a dynamically generated script in the browser in the current version is not provided.
Conclusion
Each of the libraries presented successfully solves the problem of client-side routing. The choice in favor of this or that tool depends on your requirements and preferences. RouteJs focuses on solving the problem of url-routing, without doing anything extra. JsAction, in contrast, offers a higher level and flexible approach to the execution of ajax requests, providing its own interface.
Additional links:
- To solve the problem raised in the article, you can also use the RazorJs library, which allows you to insert Razor into JavaScript files;
- https://github.com/zowens/ASP.NET-MVC-JavaScript-Routing another library for migrating routes to the client-side;
- Article by Phil Haack with reference to his library.