Posts
208
Comments
1144
Trackbacks
51
MVC DeleteLink with AjaxHelper

Relatively recently it was discovered that the MVC framework was inadvertently leading to some bad practices around deleting resources with HTTP GET requests.  Specifically, HTTP best practices (and RESTful best practices) state that GET requests should never modify resources. Some people consider this a “security” hole and, while that may be true, I consider it more of a “best practices” hole.  Stephen Walther has a great post on this topic here. In his post, Walther demonstrates two different alternatives to using a normal HtmlHelper ActionLink: 1) Ajax Deletes and 2) nested forms with image buttons.

In this post, I’m simply going to show an alternative implementation to his first option of the Ajax Delete.

Let’s say you have a list of contacts in a grid like this:

with markup that looks like this:

   1:  <%=Html.ActionLink("Add New Contact", "Create") %>
   2:   
   3:  <table cellspacing="0" cellpadding="4" border="1">
   4:      <tr>
   5:          <th scope="col">First Name</th>
   6:          <th scope="col">Last Name</th>
   7:          <th scope="col">Email</th>
   8:          <th scope="col">&nbsp;</th>
   9:          <th scope="col">&nbsp;</th>
  10:      </tr>
  11:   
  12:      <%foreach (GetContactListResult contact in this.Model) { %>
  13:          <tr>
  14:              <td><%=contact.FirstName %></td>
  15:              <td><%=contact.LastName %></td>
  16:              <td><%=contact.Email %></td>
  17:              <td>
  18:                  <%=Html.ActionLink("Edit", "Create", new { id = contact.ContactID }) %>
  19:              </td>
  20:              <td>
  21:                  <%=Html.ActionLink("Delete", "Delete", new { id = contact.ContactID }) %>
  22:              </td>
  23:          </tr>
  24:      <% } %>
  25:  </table>

On line #21 you will see the line that is the common offender – a hyperlink which will result in a GET request to delete a record to this corresponding Action Method:

   1:  public RedirectToRouteResult Delete(int id)
   2:  {
   3:      this.contactManager.DeleteContact(id);
   4:      return RedirectToAction("Index");
   5:  }

In Walther’s post, he uses the raw Sys.Net.WebRequest object to perform his Ajax operations.  Here I’m going to start out an alternative implementation but using an AjaxHelper. First off, we can change line #21 above to this:

   1:  <%=Ajax.ActionLink("Delete", "Delete", new { id = contact.ContactID }, new AjaxOptions
   2:                                                                          {
   3:                                                                              Confirm = "Are you sure you want to delete?",
   4:                                                                              OnComplete = "deleteComplete",
   5:                                                                              HttpMethod = "DELETE"
   6:                                                                          })%>

The first three arguments (link text, action name, and route values) match the original implementation exactly but note the AjaxOptions in the fourth parameter. We have a nice little confirm message that will be displayed in a JavaScript prompt. You also see that the OnComplete property is pointing to a function delegate called “deleteComplete” to invoke after the Ajax call has completed.  Finally, we’re specifying “DELETE” for the HTTP verb.  There are a couple of additional items that you have to do to make this work.  Specifically, we need to add this script block to the head section of our page:

   1:  <script src="/Scripts/MicrosoftAjax.js" type="text/javascript"></script>
   2:  <script src="/Scripts/MicrosoftMvcAjax.js" type="text/javascript"></script>
   3:   
   4:  <script type="text/javascript">
   5:      function deleteComplete() {
   6:          window.location.reload();
   7:      }
   8:  </script>

Notice we have to include the Microsoft javascript libraries to ensure the the AjaxHelper methods will work correctly. Also, I’m defining a simple callback to reload the page. If you refer back to my original Delete() action method, it is returning a RedirectToRouteResult.  Additionally, we now need to make a couple of changes to our Delete() action method like this:

   1:  [AcceptVerbs(HttpVerbs.Delete)]
   2:  public ContentResult Delete(int id)
   3:  {
   4:      this.contactManager.DeleteContact(id);
   5:      return this.Content(string.Empty);
   6:  }

Notice it is now allowing *only* requests for the DELETE verb. If a GET request for this URI is issued, it will result in a 404. I also have to return an empty ContentResult since the javascript is now going to do my redirect client side.  So this will be an incredibly lightweight Ajax server call.

As another alternative, you could even write your action method using an EmptyResult type like this:

   1:  [AcceptVerbs(HttpVerbs.Delete)]
   2:  public EmptyResult Delete(int id)
   3:  {
   4:      this.contactManager.DeleteContact(id);
   5:      return null;
   6:  }

Now when we click the delete link, we can see the request via the Web Development Helper IE add-in:

You’ll see we’re now issuing the request with a DELETE verb and the server is returning a 200. Behind the scenes, the AjaxHelper is using the XMLHttpRequest object.  One little side note, it order to make the request show up correctly in the web dev helper add-in, I had to change line #5 of my Action method to this:

   1:  return this.Content(" ");

I’m considering this a bug in the web dev helper because both an empty string and a space work just fine at run-time.

This is all well and good but the one thing is that our AjaxHelper ActionLink is a little verbose.  If I’m going to be having Delete links in multiple places in my app, I sure don’t want to have to repeat this same thing all over the place. I also don’t want to have to put in the same 1-line javascript callback function every time just to do a little redirect. In order to accomplish this, I can write my own little AjaxHelper method called “DeleteLink” that will allow me to change my mark-up to the much simpler version *and* avoid having to include an inline javascript callback like this:

   1:  <%=Ajax.DeleteLink("Delete", "Delete", new { id = contact.ContactID }) %>

Creating your own Html or Ajax Helper in MVC is typically a pretty straightforward process (*much* easier than creating ASP.NET server controls for example). The complete implementation for my DeleteLink helper method is:

   1:  public static string DeleteLink(this AjaxHelper ajaxHelper, string linkText, string actionName, object routeValues)
   2:  {
   3:      return ajaxHelper.ActionLink(linkText, actionName, routeValues, new AjaxOptions
   4:      {
   5:          Confirm = "Are you sure you want to delete this item?",
   6:          HttpMethod = "DELETE",
   7:          OnSuccess = "function() { window.location.reload(); }"
   8:      });
   9:  }

Notice that I’ve inlined the javascript function to reload the page.  Other than that, everything has just been moved into this method to encapsulate it here.  This method could be further customized to include overloads for htmlAttributes, alternative link text, or more. You could also extend this sample by making it more robust by adding a callback for the OnFailure property.

posted on Monday, April 6, 2009 9:19 PM Print
Comments
Gravatar
# re: MVC DeleteLink with AjaxHelper
MotoWilliams
4/10/2009 1:57 PM
Good article. That "window.location.reload();" seems a little brute force to me. Since you committed to a Ajax way of doing things in this sample why not just remove that 'row' from your table in the OnSuccess callback with JQuery or something of the like?
Gravatar
# re: MVC DeleteLink with AjaxHelper
Steve
4/15/2009 10:37 AM
@MotoWilliams - In general, I follow the PRG pattern (http://devlicio.us/blogs/tim_barcz/archive/2008/08/22/prg-pattern-in-the-asp-net-mvc-framework.aspx) for this type of thing but you are correct - given that this is already "pure" AJAX you could just remove the row with jQuery. In that case the only caveat might be that if you have alternate row highlighting or stuff like that, you may have some more work to do sorting out display issues. But you are definitely correct - it is a viable option. Thanks.
Gravatar
# re: MVC DeleteLink with AjaxHelper
mazhar kaunain baig
6/13/2010 2:13 PM
How can i do the acknowledgment stuff like record is successfully deleted or there is a error in deleting the record
Gravatar
# re: MVC DeleteLink with AjaxHelper
Steve
6/13/2010 10:35 PM
@mazhar - If you want to show a message like that, then the PRG pattern with window.location.reload() is not the best option. In that case, the simplest approach is to use the UpdateTargetId as part of the AJAX ActionLink as shown here: http://msdn.microsoft.com/en-us/library/system.web.mvc.ajaxhelper.aspx

Post Comment

Title *
Name *
Email
Comment *  
Verification

View Steve Michelotti's profile on LinkedIn

profile for Steve Michelotti at Stack Overflow, Q&A for professional and enthusiast programmers




Google My Blog

Tag Cloud