Implementing a Delete Link with MVC 2 and HttpMethodOverride

Implementing a Delete Link with MVC 2 and HttpMethodOverride

A while back I blogged about creating an MVC Delete Link with the AjaxHelper. This was based on another blog post from Stephen Walther where he explained the drawbacks of using hyperlinks for delete scenarios.  HTTP and REST best practices state that GET requests should never modify a resource. The most “RESTful” implementation is is use a DELETE verb. In Walther’s post he shows two primary examples: 1) using AJAX to issue a true “DELETE” request, and 2) using individual forms to do the delete operations. In my previous post on MVC DeleteLink, I essentially showed a variation of his first example (i.e., the AJAX method). In this post I’ll show a variation on his second example using MVC 2 and the new HttpMethodOverride (i.e., using individual forms for the delete).

An HTML form supports only GET and POST for the form action. It does not support PUT or DELETE verbs. Hence, the typical solution to this is the leverage the form POST to do delete operations as well (in effect, “overloading” the meaning of form POST). MVC 2 introduces a new HTML helper called HttpMethodOverride which allows your form POST to “act like” another verb. This is a common convention that is used by other frameworks (Ruby on Rails does something similar) – sometimes passing “X-HTTP-Method-Override” in an HTTP header and sometimes passing it in a hidden form variable like this. Therefore, if we want a simple delete link, we can now do it like this:

1: <%Html.BeginForm("Delete", "Contacts", new { id = workout.Id }); %>

2: <input type="image" src="/images/remove-icon.png" alt="Delete" />

3: <%=Html.HttpMethodOverride(HttpVerbs.Delete) %>

4: <%Html.EndForm(); %>

This will render HTML output like this:

1: <form action="/Contacts/Delete/1" method="post">

2: <input type="image" src="/images/remove-icon.png" alt="Delete" />

3: <input name="X-HTTP-Method-Override" type="hidden" value="DELETE" />

4: </form>

The best part about this is the the MVC action method attributes will respect this form variable:

1: [HttpDelete]

2: public ActionResult Delete(int id)

3: {

4: this.repository.Delete(id);

5: return this.RedirectToAction("Index");

6: }

Notice the [HttpDelete] attribute. This is also new in MVC 2 as a more succinct version of [AcceptVerbs(HttpVerbs.Delete)]. This is a much better and cleaner implementation than we had available in MVC 1.  However, needing a delete link is something that is going to be pretty common in your applications and creating a form, an input tag and the HttpMethodOverride() is a lot to go through every time you need it. As always, the best approach to keep things simpler (and more DRY) is to create your own re-usable HTML helper method that will allow you to call it like this:

1: <%=Html.DeleteLink("Delete", "Workouts", new { id = workout.Id }, "/images/remove-icon.png") %>

The complete code for that HTML helper method:

1: public static MvcHtmlString DeleteLink(this HtmlHelper helper, string actionName, string controllerName, object routeValues, string imageUrlPath)

2: {

3: var urlHelper = new UrlHelper(helper.ViewContext.RequestContext);

4: var url = urlHelper.Action(actionName, controllerName, routeValues);

5:  

6: var formTag = new TagBuilder("form");

7: formTag.MergeAttribute("action", url);

8: formTag.MergeAttribute("method", "post");

9:  

10: var inputTag = new TagBuilder("input");

11: inputTag.MergeAttribute("type", "image");

12: inputTag.MergeAttribute("src", imageUrlPath);

13: inputTag.MergeAttribute("alt", "Delete");

14:  

15: formTag.InnerHtml = inputTag.ToString(TagRenderMode.SelfClosing) + helper.HttpMethodOverride(HttpVerbs.Delete);

16:  

17: return MvcHtmlString.Create(formTag.ToString());

18: }

Also notice this is returning the new MvcHtmlString type rather than just a string.

So does this allows us to be more “RESTful”? Yes, it does. But we could still go further.  I really like the approach by Dylan Beattie here in which he introduces route constraints so the URL’s don’t need to contain segments like “/Delete” or “/Edit”. His approach could be further generalized for *all* controllers such that any POST request that came in with and X-HTTP-Method-Override of “DELETE” would automatically be routed to an action method named Delete(). This is a convention that makes a lot of sense and could also be further enforced with a layer supertype controller.

posted on Friday, January 8, 2010 11:57 PM Print

This article is part of the GWB Archives. Original Author: Steve Michelotti

New on Geeks with Blogs

  • We Won The One Award I Actually Care About

    Full Scale made the Inc. 5000 for the fifth year straight, the 12th listing across my three companies. Here is why the one award you cannot buy is worth stopping for.

  • Your Customers Build the Features Now

    I let a tool I liked sit dead for a year rather than build the features I wanted. An MCP server meant I never had to, and your customers can do the same to your product.

  • Get the Size of a Directory in Linux the Easy Way

    du -sh for the quick answer, ncdu for the cleanup, df for the disk itself: every command for checking directory size in Linux, plus why du and df never agree.

  • Vim Search and Replace: The Ultimate Guide

    One :%s command replaces every match in a file before a find dialog would even open. The Vim substitute patterns worth the muscle memory: flags, ranges, capture groups, and multi-file edits.