views:

256

answers:

1

I've just started to use Jersey to create a RESTful API for my site. Its a wonderful change from having to roll my own support for RESTful services in Java. One thing I just can't seem to figure out is how to "fake" a DELETE and PUT method.

Jersey supports the annotations @PUT and @DELETE, however many Load-Balancers will not allow these methods through. In the past I've relied on the ability to define a custom HTTP header (e.g. x-method-override: DELETE) and "tunneling" within a POST request.

Has anyone found a way to bind a method using Jersey/JAX-RS annotations to custom headers? Alternatively, is there a better way around lack of support for PUT and DELETE?

+1  A: 

Well here is how I've decided to handle the situation within my API. Its relatively simple and doesn't require much additional coding. To illustrate consider a RESTful api for Address:

@Path("/address")
public class AddressService {

    @GET
    @Produces("application/xml")
    public StreamingOutput findAll() { ... }

    @POST
    @Produces("application/xml")
    @Consumes("application/x-www-form-urlencoded")
    public StreamingOutput create(...) { ... }

    //
    // This is the alternative to a "PUT" method used to indicate an "Update"
    // action.  Notice that the @Path expects "/id/{id}" which allows 
    // us to bind to "POST" and not get confused with a "Create"
    // action (see create() above).
    //
    @POST
    @Produces("application/xml")
    @Consumes("application/x-www-form-urlencoded")
    @Path("/id/{id}")
    public StreamingOutput update(@PathParam("id") Long id, ...) { ... }

    //
    // This is the typical "GET" method with the addition of a check
    // for a custom header "x-method-override" which is designed to 
    // look for inbound requests that come in as a "GET" but are 
    // intended as "DELETE".  If the methodOverride is set to "DELETE"
    // then the *real* delete() method is called (See below)
    //
    @GET
    @Produces("application/xml")
    @Path("/id/{id}")
    public StreamingOutput retrieve(
      @PathParam("id") Long id, 
      @HeaderParam("x-method-override") String methodOverride)
    {
      if (methodOverride != null && methodOverride.equalsIgnoreCase("DELETE")) {
        this.delete(id);
      }

      ...
    }


    // 
    // This is the typical "DELETE" method.  The onlything special about it is that
    // it may get invoked by the @GET equivalent is the "x-method-override" header
    // is configured for "DELETE"
    //
    @DELETE
    @Produces("application/xml")
    @Path("/id/{id}")
    public StreamingOutput retrieve(@PathParam("id") Long id) { ... }

}

raiglstorfer
Hi. First a small thing: the delete method is named 'retrieve'.Wouldn't it be better to use POST instead of GET for a fake DELETE since GET request must always be safe.Another thing: Is there way to use fake deletes and puts from within HTML Forms? There is no way to add the x-method-override header. The only thing possible is a hidden '_method' field in the form.
Florian
Great points. I really haven't come up with a better solution at this point. Hidden form elements are very limited as many posts contain XML vs. encoded form parameters. That said, most of my RESTful services are invoked via XMLHTTPRequest (or indirectly via frameworks like JQuery) so setting up headers is very simple and always possible. For this reason mainly I stick with my solution.
raiglstorfer