X-Http-Method-Override with NancyFX

posted on 11 Feb 2013 | NancyFX

Forms in HTML only allow you to use the method's POST and GET, I'm not sure if this would be classified as a limitation or not, but it does introduce a slight problem when you want to create a nice API taking advantage of verbs such as PUT and DELETE, when you can only POST and GET.

This means if you want to delete something you have to use a different URL, like:

/products/123/delete

Then define a route like

Post["/products/{id}/delete"] = _ =>
{
    ... Do Something...
};

This isn't exactly ideal, what we would prefer to have is a more semantic route.

  • GET -> gets an object
  • DELETE -> deletes an object
  • PUT -> modifies an object
  • PATCH -> modifies part of an object
  • POST -> creates an object

Note: I'm no expert on this stuff so the VERBs above may not be correct wording.

This would allow us to define the same route like so

Delete["/products/{id}"] = _ =>
{
    ... Do Something...
};

Since HTML forms only allow GET/POST, the web framework needs to do a little bit of trickery in order to send a request to the correct route/verb. In this case we want to use a POST but have it go to a DELETE route.

MVC offers the ability to use the HttpMethodOverride helper to add a hidden input field to a form

@Html.HttpMethodOverride(HttpVerbs.Delete)

This will generate a hidden field like so

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

NancyFX also allows you to override the Method, but the field is actually named _method

<input type="hidden" name="_method" value="DELETE"/>

Note: I'm not actually sure why the NancyFX team decided to name it _method rather than X-HTTP-Method-Override, though from reading it seems if you're posting a form it should be _method, but if you're sending it via headers it should be X-HTTP-Method-Override (turns out they followed Sinatra)

Give a super basic scenario, I defined a module

public ProductsModule() : base("products")
{
    Get["/"] = _ => View["index", Products];

    Delete["/{id}"] = _ =>
    {
        Products.Remove(Products.Single(x => x.Id == (int)_.id));

        return View["index", Products];
    };
}

Products is just a static list products. The view contains the following

<ul>
  @Each.Model
  <li>
      <form action="/products/@Current.Id" method="POST">        
        <label>@Current.Name</label>
        <input type="submit" value="Delete"/>
      </form>   
  </li> 
  @EndEach
</ul>

Note: This demo is using the Nancy Super Simple View Engine, not Razor :)

This renders the page like

Clicking on the delete button

As you can see, it doesn't hit the Delete route, so if we update the View to contain the _method field

<ul>
  @Each.Model
  <li>
      <form action="/products/@Current.Id" method="POST">
        <input type="hidden" name="_method" value="DELETE"/>

        <label>@Current.Name</label>
        <input type="submit" value="Delete"/>
      </form>   
  </li> 
  @EndEach
</ul>

And now click the delete button again

We can see that it hits the correct route.

And the item (Product 4) has been removed from the list. So that's it, super simple to override the Method verb in NancyFX :)

Note: At the time of writing this, version 0.15.3 only supports _method on form posts, but the next version will support both X-HTTP-Method-Override and _method on both Headers and Forms.

comments powered by Disqus