|
|
|
MVC .NET URL Generation Done Right! By Matt HopkinsWeakly-typed URLs can and should be avoided in MVC .Net applications. MVC .Net provides methods for generating type-safe URLs on the back-end, but these methods fall short when it comes to dynamic URLs generated via JavaScript. In modern web applications, dynamic URLs are commonly required and are a frequent source of errors. These problems are exacerbated by the difficulty of automatically testing this portion of the application. The strongly-typed dynamic URL generation techniques in this article eliminate the majority of these issues without the cost of any additional developer or test resource time.Strongly-Typed vs Weakly-Typed Static URL GenerationMVC .Net provides a couple of ways to generate links. Html.ActionLink(“View Items”, new {Controller = “InventoryItem”, Action = “List”, Category = 1}) The example above presents a typical weakly-typed link. Notice how an anonymous type is used to determine the controller, action, and action parameters. This anonymous type has no restrictions and isn’t capable of having any compile time knowledge of the InventoryItemController class. This should make your stomach cringe and developers should have higher standards than this. You’ve already gone through the trouble of defining the Class named InventoryItemController, a function named View, and even defined the function signature for View. Why abandon all of this information that is already known at compile time? The alternative is: Html.ActionLink<InventoryItemController>(c => c.List(1), “View Items”) This demonstrates a much more dependable means of link generation. It doesn’t just assume that a subclass of Controller named InventoryItemController has an action named List. This approach guarantees that InventoryItemController and List exists and that List takes an int as an argument. By doing so, this approach enables Intellisense, avoids type-os, is more concise, and gives you the ability to refactor. What about Dynamic URLs?The link and URL generation methods that ship with MVC .Net or MvcContrib don’t work in practice half of the time. When you actually sit down to write an application using MVC .Net, it becomes strikingly clear that many of your URL’s end up being generated in the browser using JavaScript. Consider the following use case of a storefront that allows you to buy a shirt. You want your customers to be able to select the shirt style and color, then add it to their shopping cart. The image of the shirt should change to reflect the customer’s selected preferences.
How would you code a page like this in MVC .Net? You could try something like:
11 <div id="shirtParams"> 12 <% using (Ajax.BeginForm("CloseUp", new AjaxOptions { HttpMethod = "Get", UpdateTargetId = "preview", InsertionMode = InsertionMode.Replace })) { %> 13 <p> 14 Style: <select name="style" id="style"> 15 <option value="0">V-Neck</option> 16 <option value="1">Crew</option> 17 </select> 18 </p> 19 <p> 20 Color: <select name="color" id="color"> 21 <option value="0">Red</option> 22 <option value="1">Green</option> 23 <option value="2">Yellow</option> 24 </select> 25 </p> 26 <input type="submit" style="display:none" id="hiddensubmit"></input> 27 <script type="text/javascript"> 28 $(function () { 29 $('#style').add('#color').change(function () { 30 //MVC's ajax methods don't handle form submission via javascript correctly, so we 31 //use a hidden submit button's click event as a workaround 32 $('#hiddensubmit').click(); 33 }).change(); 34 }); 35 </script> 36 <%} %> 37 </div> 38 <div id="preview"></div> 39
The example above is hideous for many reasons: Most developers would result to using javascript to manually generate the ajax call. 9 <div id="shirtParams"> 10 <p> 11 Style: <select name="style" id="style"> 12 <option value="0">V-Neck</option> 13 <option value="1">Crew</option> 14 </select> 15 </p> 16 <p> 17 Color: <select name="color" id="color"> 18 <option value="0">Red</option> 19 <option value="1">Green</option> 20 <option value="2">Yellow</option> 21 </select> 22 </p> 23 </div> 24 <div id="preview"></div> 25 <script type="text/javascript"> 26 $('#style').add('#color').change(function () { 27 $.ajax({ 28 type: 'GET', 29 url: '<%=Url.Action("CloseUp", "Shirt")%>?style=' + $('#style').val() + '&color=' + $('#color').val(), 30 success: function (data) { 31 $('#preview').html(data); 32 } 33 }); 34 }).change(); 35 </script> 36 This approach has its own drawbacks: The SolutionEither of the above methods are sufficient for large scale projects. Instead, I am advocating the use helper methods for type-safe dynamic URL generation. This technique makes the following example possible: 10 <div id="shirtParams"> 11 <p> 12 Style: <select name="style" id="style"> 13 <option value="0">V-Neck</option> 14 <option value="1">Crew</option> 15 </select> 16 </p> 17 <p> 18 Color: <select name="color" id="color"> 19 <option value="0">Red</option> 20 <option value="1">Green</option> 21 <option value="2">Yellow</option> 22 </select> 23 </p> 24 </div> 25 <div id="preview"></div> 26 <script type="text/javascript"> 27 $('#color').add('#style').change(function(){ 28 var style = $('#style').val(); 29 var color = $('#color').val(); 30 <%= this.JsLoadElement<ShirtController, ShirtColorsEnum, ShirtStylesEnum>((c, color, style) => c.CloseUp(color, style), 31 new JsLoadOptions{ 32 JsTargetSelector = "$('#preview')", 33 InsertionMode = JsLoadInsertionMode.Replace 34 })%> 35 }).change(); 36 </script> 37 This approach surpasses the previous examples in the following ways: • Everything is type checked The Take-AwayThis article has shown how ad-hoc URL creation is impractical and avoidable. ASP .Net MVC applications are more stable, maintainable, and flexible when type-safe dynamic URL generation is used. These techniques should be included in any MVC developers toolbox. Not only do these techniques make make it easier to harness MVC .Net’s power, they allow creation of invaluable helper methods for widgets that were previously difficult or impractical.
Matt Hopkins works with growing businesses to evaluate and implement new solutions that give them more room to grow. His experience has spanned everything from evaluation and proof-of-concept LMS systems to development and integration of a web-based resource management application. Matt is also the co-creator of nofouls.com, a social networking site for pick-up basketball players. |




