10

I have the following server-side code in Jersey 2.x:

@Path("/store/remove/from/group")
@DELETE
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Produces(MediaType.TEXT_PLAIN)
public Response removeStoresFromGroup(@FormParam("storeName") List<String> storeNames, @FormParam("groupName") String groupName) {
    //......
}

On client side, I want to use Jersey 2.x client to send a delete request to the above web service. However, from the documentation of Jersey client API, I didn't find how to enclose the following data in DELETE request:

WebTarget webTarget = client.target("/store/remove/from/group");
MultivaluedMap<String, String> formData = new MultivaluedHashMap<String, String>();
List<String> storeName = new ArrayList<String>();
storeName.add("Store1");
storeName.add("Store2");
storeName.add("Store3");

formData.addAll("storeName", storeName);
formData.add("groupName", "Group A");

Response response = webTarget.request().accept(MediaType.TEXT_PLAIN).delete();   //The delete() method doesn't take any entity body in the request.

From the Jersey client API, the SyncInvoker class doesn't support a delete method with entity body as its argument. So I can only use either POST or PUT to send the data to the server like the following (but not for DELETE):

Response response = webTarget.request().accept(MediaType.TEXT_PLAIN).post(Entity.form(formData)); 

But I want to use DELETE request since the request is deleting some resources. How to send DELETE request with some entity data via Jersey client?

Sam
  • 7,252
  • 16
  • 46
  • 65
tonga
  • 11,749
  • 25
  • 75
  • 96
  • 1
    OT but normally you don't need to send an entity with a DELETE [because](http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.7) "The DELETE method requests that the origin server delete the resource identified by the Request-URI". Your method reference sounds more like updating a reference then deleting a resource. – lefloh Aug 10 '14 at 17:08
  • Sometimes if I want to delete some data which satisfies certain conditions, I need to pass in some parameters in the entity body of DELETE request. – tonga Aug 10 '14 at 21:07

5 Answers5

24

Based on the code in Jersey 2.18 version, The class JerseyInvocation use a predefined HashMap to validate HTTP method and its Entity as below:

map.put("DELETE", EntityPresence.MUST_BE_NULL);
map.put("GET", EntityPresence.MUST_BE_NULL);
...

That's why we got this error "Entity must be null for http method DELETE".

While, please note that it also provide a Jersey client configuration property ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION to determine whether to stop the rest execution or not, so here we can use this property to suppress validation in order to continue to send a DELETE request with Entity. e.g.

    ClientConfig config = new ClientConfig();
    config.property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true);
    Client client = ClientBuilder.newClient(config);
    ...
CarlJi
  • 251
  • 2
  • 5
  • I have a similar need to do this, although in my case I don't actually have a body to send, but I do need to force a 'Content-Length: 0' to be sent with a DELETE. Trying this suggestion with Jersey Client 2.5.2, I get a ProtocolException: java.net.ProtocolException: HTTP method DELETE doesn't support output at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(HttpURLConnection.java:1081) at org.glassfish.jersey.client.HttpUrlConnector$3.getOutputStream(HttpUrlConnector.java:267) – Kevin Hooke Jan 08 '16 at 01:35
  • 2
    My attempt to override org.glassfish.jersey.test.JerseyTest#configureClient with ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION set to true was turned into a failure. There is no exception anymore but instead I'v got service response with 400. – Bender Dec 21 '17 at 11:38
  • I'm converting a server application from Play Framework to pure JavaEE and had a DELETE resource that took a JSON body. I can't just break the old API so this helped me! – matsa Dec 06 '18 at 14:48
9

Just to note: if you use resteasy implementation of JAX RS Client API, you may use build().invoke():

client.target("$baseUrl$restEndPoint/$entityId")
                .request("application/json")
                .build("DELETE", Entity.entity(entity, MediaType.APPLICATION_JSON))
                .invoke()

But it does not work with jersey

Alexandr
  • 9,213
  • 12
  • 62
  • 102
5

A DELETE with an entity body is not strictly forbidden but it's very uncommon and ignored by some frameworks/servers. The need of an entity body may indicate that a DELETE is not used as it is intended.

For instance: If a GET /customers/4711 returns one customer and you send a DELETE /customers/4711 the next GET on this resource should return a 404. You deleted a resource identified by a URL like defined in the spec.

Your URL /store/remove/from/group does not seem to identify a resource. Using identifiers like /store/4711 or /groups/4711 and sending a DELETE on them would not fit your needs because you want to "remove a store from a group" not delete a store or a group.

Assuming you have a group resource

{
  "id" : 4711,
  "stores" : [123, 456, 789]
}

and you want a result like

{
  "id" : 4711,
  "stores" : [123, 789]
}

you are not deleting anything. You are modifying a resource so PUT, POST or PATCH are appropiate methods. JSON-Patch is a good format for describing such changes. A request would look like this:

PATCH /groups/4711 HTTP/1.1
Content-Type: application/json-patch

[
  {
    "op" : "remove"
    "path" : "stores/1"
  }
]
Community
  • 1
  • 1
lefloh
  • 10,653
  • 3
  • 28
  • 50
  • Thanks for your suggestions. It seems that `PATCH` method is not currently supported by Jersey framework. Also does Java web server like Tomcat or Jetty support HTTP PATCH method? I could use PUT request in this case since I'm modifying the resource. – tonga Aug 11 '14 at 14:29
  • I'm sorry, I don't have an overview which container or framework supports `PATCH`. `PUT` is a good idea but this has the downside that you need to [send the whole modified resource](http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.6). – lefloh Aug 11 '14 at 14:45
  • Thanks. For the sake of stability, I will just use http `PUT` for now and hopefully http `PATCH` method will be officially included in later Jersey release. – tonga Aug 11 '14 at 14:52
3

You can use webTarget.request().accept(MediaType.TEXT_PLAIN).method("DELETE",yourEntity) to invoke a DELETE with an entity in it.

John Ament
  • 11,595
  • 1
  • 36
  • 45
  • 4
    When I used this method, it reports error: "Entity must be null for http method DELETE". Why is that? – tonga Aug 10 '14 at 21:05
  • Hmm I honestly wasn't expecting that. If you look at [1] at section 9.6 you'll see that `DELETE` is meant to be URI based. It doesn't say it can't use a body though.. [1]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html – John Ament Aug 10 '14 at 22:17
  • I don't see why DELETE request can't contain entity body either from the doc. But the javadoc (http://docs.oracle.com/javaee/7/api/javax/ws/rs/client/SyncInvoker.html#method) doesn't say it cannot use DELETE as the method name either. – tonga Aug 11 '14 at 00:44
0

I used also RESTeasy Jersey client and worked for my:

Response response = client.target(yourUrlToCall).request(MediaType.APPLICATION_JSON) .build("DELETE", Entity.entity(yourJsonDTO, MediaType.APPLICATION_JSON)).invoke();

Is very usefull to solve some complex scenarios that are not "allowed" by normal usage of get(), delete(), etc.

Luis
  • 43
  • 1
  • 4