Html.ActionLink and HTTP PUT, DELETE verbs

Recently I was developing an ASP.NET MVC application in which I needed to create clickable images that would make it possible to delete items and/or to switch an items state between activated/deactivated. I wanted to solve this by using controller methods that would be decorated with an [HttpDelete] or [HttpPut] attribute (delete and put http verbs). Creating the controller methods was straightforward, but I needed to add some html to my view to have the url submitted/posted with the correct http verb (delete or put). Ideally I would want to use Html.ActionLink for this, but unfortunatelly it doesn't support clickable images (only '<a href=...'), and it also does not support other http verbs then 'get'. I have created an HTML helper class, with some HTML extension methods, to make it easier to add clickable put/delete images to views, which I will describe in the remainder of this post.

The code listing below shows (example) controller methods, that I want to be able to call from the user's browser.

[HttpPut]
public ActionResult PutMethod(int putId)
{
    ViewBag.Message = string.Format(CultureInfo.InvariantCulture, "Put verb<br/>put id:{0}", putId);
    return View("Index");
}

[HttpDelete]
public ActionResult DeleteMethod(int deleteId)
{
    ViewBag.Message = string.Format(CultureInfo.InvariantCulture, "Delete verb<br/>delete id:{0}", deleteId);
    return View("Index");
}

We need to add the following HTML to the page, in order to call the methods on the controller.

<form action="/Home/PutMethod?putId=26" method="post">
   <input name="X-HTTP-Method-Override" type="hidden" value="PUT" />
   <input alt="Put" src="/Content/26.jpg" type="image" />
</form>

<form action="/Home/DeleteMethod?deleteId=46" method="post">
   <input name="X-HTTP-Method-Override" type="hidden" value="DELETE" />
   <input alt="Delete" src="/Content/46.jpg" type="image" />
</form>

An example of the 'standard' way to create these HTML forms is shown in the listing below.

<%using (Html.BeginForm("PutMethod", "Home", new { putId = 26 }, FormMethod.Post))
{ %>
    <%= Html.HttpMethodOverride(HttpVerbs.Put) %>
    <input alt="Put" src="<%= ResolveUrl("~/Content/26.jpg") %>" type="image" title="Test put" />
<% } %>
<%using (Html.BeginForm("DeleteMethod", "Home", new { deleteId = 46 }, FormMethod.Post))
{ %>
    <%= Html.HttpMethodOverride(HttpVerbs.Delete) %>
    <input alt="Delete" src="<%= ResolveUrl("~/Content/46.jpg") %>" type="image" onclick="return confirm('Are you sure?');"  title="Test delete" />
<% } %>

We need to add a lot of html/aspx markup to create a simple clickable put/delete image. I have made several HTML extension methods to make the generation of the clickable put/delete images smaller and easier to use. An example usage of my extension methods is shown in the following code listing.

<%= Html.PutLink("PutMethod", "Home", ResolveUrl("~/Content/26.jpg"), new { putId = 26 })%>
<%= Html.DeleteLink("DeleteMethod", "Home", ResolveUrl("~/Content/46.jpg"), new { deleteId = 46 })%>

And another example with some additional HTML attributes:

<%= Html.PutLink("PutMethod", "Home", ResolveUrl("~/Content/27.jpg"), new { putId = 27 }, new { title = "Test put" })%>
<%= Html.DeleteLink("DeleteMethod", "Home", ResolveUrl("~/Content/24.jpg"), new { deleteId = 24 }, new { title = "Test delete", onclick = "return confirm('Are you sure?');" })%> 

The markup in the view becomes smaller and easier to understand, by using these extension methods.  The extension methods require the action and controller names, the path of the image, the route values (null is allowed), and optional html attributes. Both the DeleteLink and PutLink extension methods call a static method that I have called DeletePutLink. Apart from a HTTP verb parameter, this method has the same parameters as the DeleteLink and PutLink methods.

In the DeletePutLink we are creating an HTML form, to which we add the 'X-HTTP-Method-Override' input element. Of course we will also add the image input element to the form. We will use the HttpMethodOverride method of ASP.NET MVC's HtmlHelper class to generate the 'X-HTTP-Method-Override' inputs html. We are using TagBuilders to create the html of the form and the image input elements. The route values (controller method parameters) and html attributes will be added to the image input element. The complete code listing of the Html helper class, can be found in the code listing below.

namespace System.Web.Mvc
{
    using System.Collections.Generic;
    using System.Globalization;
    using System.Web.Routing;
    using System.Web.UI;
   
    public static class HtmlExtensions
    {
        public static MvcHtmlString DeleteLink(this HtmlHelper helper, string actionName, string controllerName, string imageUrlPath, object routeValues)
        {
            return DeleteLink(helper, actionName, controllerName, imageUrlPath, routeValues, null);
        }

        public static MvcHtmlString DeleteLink(this HtmlHelper helper, string actionName, string controllerName, string imageUrlPath, object routeValues, object htmlAttributes)
        {
            return DeletePutLink(HttpVerbs.Delete, helper, actionName, controllerName, imageUrlPath, routeValues, htmlAttributes);
        }

        public static MvcHtmlString PutLink(this HtmlHelper helper, string actionName, string controllerName, string imageUrlPath, object routeValues)
        {
            return PutLink(helper, actionName, controllerName, imageUrlPath, routeValues, null);
        }

        public static MvcHtmlString PutLink(this HtmlHelper helper, string actionName, string controllerName, string imageUrlPath, object routeValues, object htmlAttributes)
        {
            return DeletePutLink(HttpVerbs.Put, helper, actionName, controllerName, imageUrlPath, routeValues, htmlAttributes);
        }

        public static MvcHtmlString DeletePutLink(HttpVerbs verb, HtmlHelper helper, string actionName, string controllerName, string imageUrlPath, object routeValues, object htmlAttributes)
        {
            // Get the url to the controller method
            var urlHelper = new UrlHelper(helper.ViewContext.RequestContext);
            var url = urlHelper.Action(actionName, controllerName, routeValues);

            // Create the form
            var form = new TagBuilder("form");
            form.Attributes["action"] = url;
            form.Attributes["method"] = "post";

            // Add X-HTTP-Method-Override to the form
            string formHtml = helper.HttpMethodOverride(verb).ToHtmlString();

            // Create the clickable image
            var inputImage = new TagBuilder("input");
            inputImage.Attributes["type"] = "image";
            inputImage.Attributes["src"] = imageUrlPath;
            inputImage.Attributes["alt"] = verb.ToString();
            if (htmlAttributes != null)
            {
                inputImage.MergeAttributes<string, object>(new RouteValueDictionary(htmlAttributes));
            }

            // Return the html form
            formHtml+= inputImage.ToString(TagRenderMode.SelfClosing);
            form.InnerHtml = formHtml;
            return MvcHtmlString.Create(form.ToString());
        }
    }
}

In this post I have described a way to make it easier to add clickable images that will post to a ASP.Net MVC controller method with an HTTP PUT or DELETE verb. It's very easy to change the workings of the extensions methods to make other PUT/DELETE-able html elements (just change/enhance the DeletePutLink method), as you can see in my implementation.

comments