Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for DELETE with body to RestTemplate through exchange method [SPR-12361] #16966

Closed
spring-projects-issues opened this issue Oct 22, 2014 · 10 comments
Assignees
Labels
in: web type: enhancement
Milestone

Comments

@spring-projects-issues
Copy link
Collaborator

@spring-projects-issues spring-projects-issues commented Oct 22, 2014

Manuel Jordan opened SPR-12361 and commented

If I delete a resource through an id and with the delete method, it works fine

restTemplate.delete("http://localhost:8080/spring-utility/person/{id}/one", id);

But delete method is void, therefore I can't get reply and status from the server.

Just playing, through the exchange method together with HttpMethod.PUT, I can do the following:

ResponseEntity<String> response = 
			restTemplate.exchange("http://localhost:8080/spring-utility/person/{id}", 
							   	   HttpMethod.PUT, 
							      new HttpEntity<Person>(person), 
							         String.class, 
							         person.getId());

And works fine, until here two points

  1. I can get a a reply and status from the server
  2. I can send a request body

Thinking in the same idea, I have tried use the exchange method together with HttpMethod.DELETE

ResponseEntity<String> response =
		restTemplate.exchange("http://localhost:8080/spring-utility/person/{id}/two", 
						HttpMethod.DELETE, 
					        new HttpEntity<Person>(person), 
					        String.class, 
					        person.getId());	

It with the intention to

  1. get a a reply and status from the server
  2. send a request body - better control against send an id

Observe: I want send an object to be deleted, not an id.

But always I get the following:

DELETE request for "http://localhost:8080/spring-utility/person/1/two" resulted in 400 (Bad Request); invoking error handler
Exception in thread "main" org.springframework.web.client.HttpClientErrorException: 400 Bad Request

I did a research:

According with the links: Delete has no support for request body. Wondered why this behaviour
Could be improved it the API?


Affects: 4.0.7, 4.1.1

Issue Links:

  • #18637 RestTemplate doesnt support DELETE with RequestBody ("is duplicated by")
  • #12524 Add support for DELETE with body to RestTemplate
  • #16896 Add support for DELETE with body to AsyncRestTemplate
  • #18665 AsyncRestTemplate should trigger no-output HTTP requests immediately as well

0 votes, 6 watchers

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Oct 22, 2014

Rossen Stoyanchev commented

It sounds like the request is going out successfully since there is an actual response status from the server. What's the underlying reason for the 400 response? Can you find out from the server log?

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Oct 22, 2014

Manuel Jordan commented

Hi Rossen

Enabling Debug

DEBUG .springframework.web.client.RestTemplate: 78 - Created DELETE request for "http://localhost:8080/spring-utility/person/1/two"
DEBUG .springframework.web.client.RestTemplate: 670 - Setting request Accept header to [text/plain, application/json, application/*+json, /]
DEBUG .springframework.web.client.RestTemplate: 747 - Writing [Person [id=1, firstName=Manuel, lastName=Jordan, alive=true, dateBirth=Mon Jul 06 00:00:00 PET 1981, dateRetirement=Fri Jul 06 00:00:00 PET 2040, dateDeath=null, age=33, weight=75.5, salary=1500.88]] using [org.springframework.http.converter.json.MappingJacksonHttpMessageConverter@1bb266b3]
WARN .springframework.web.client.RestTemplate: 582 - DELETE request for "http://localhost:8080/spring-utility/person/1/two" resulted in 400 (Bad Request); invoking error handler

Immediately below

Exception in thread "main" org.springframework.web.client.HttpClientErrorException: 400 Bad Request
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:91)
at org.springframework.web.client.RestTemplate.handleResponseError(RestTemplate.java:589)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:547)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:503)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:445)
at com.manuel.jordan.rest.RestClient.deletePersonTwo(RestClient.java:85)
at com.manuel.jordan.main.Main.main(Main.java:59)

Where the server side is

@RequestMapping(value="/{id}/two", method=RequestMethod.DELETE)
public ResponseEntity<String> deletePersonTwo(@PathVariable Integer id, @RequestBody Person person){
	logger.info("PersonRestController  - deletePersonTwo - id: {} - {}", id, person.toString());
	personMapRepository.deletePersonTwo(id, person);
	return new ResponseEntity<>("Delete forever", HttpStatus.MOVED_PERMANENTLY);
}

Be aware about the Update works fine and has the following structure:

@RequestMapping(value="/{id}", method=RequestMethod.PUT)
public ResponseEntity<String> updatePerson(@PathVariable Integer id, @RequestBody Person person){
	logger.info("PersonRestController  - updatePerson - id: {} - {}", id, person.toString());
	personMapRepository.savePerson(person);
	return new ResponseEntity<>("Update successful", HttpStatus.OK);
}

Practically for the server side Get and Put have the same structure.

In the original post you can see how the server side is called through the RestTemplate for the Get and Put respectively with the exchange method.

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Oct 22, 2014

Rossen Stoyanchev commented

This all looks like client-side logging. Still no clues as to why the server thinks it's a bad request. Can you check the server log? Most likely there is an exception somewhere that's getting mapped to HttpServletResponse.SC_BAD_REQUEST by the DefaultHandlerExceptionResolver.

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Oct 22, 2014

Manuel Jordan commented

About the log information

In my STS directory installation exists the following directory path base-instance/logs and there is a file with only the following:

127.0.0.1 - - [22/Oct/2014:14:00:55 -0500] "GET / HTTP/1.1" 200 2653
127.0.0.1 - - [22/Oct/2014:14:01:05 -0500] "POST /spring-utility/person/ HTTP/1.1" 201 -
127.0.0.1 - - [22/Oct/2014:14:01:05 -0500] "GET /spring-utility/person/1 HTTP/1.1" 302 244
127.0.0.1 - - [22/Oct/2014:14:01:05 -0500] "PUT /spring-utility/person/1 HTTP/1.1" 200 17
127.0.0.1 - - [22/Oct/2014:14:01:05 -0500] "GET /spring-utility/person/1 HTTP/1.1" 302 244
127.0.0.1 - - [22/Oct/2014:14:01:05 -0500] "PUT /spring-utility/person/1 HTTP/1.1" 200 17
127.0.0.1 - - [22/Oct/2014:14:01:05 -0500] "GET /spring-utility/person/1 HTTP/1.1" 302 246
127.0.0.1 - - [22/Oct/2014:14:01:05 -0500] "DELETE /spring-utility/person/1/one HTTP/1.1" 301 14
127.0.0.1 - - [22/Oct/2014:14:01:05 -0500] "GET /spring-utility/person/1 HTTP/1.1" 302 -
127.0.0.1 - - [22/Oct/2014:14:01:05 -0500] "POST /spring-utility/person/ HTTP/1.1" 201 -
127.0.0.1 - - [22/Oct/2014:14:01:05 -0500] "GET /spring-utility/person/1 HTTP/1.1" 302 244
127.0.0.1 - - [22/Oct/2014:14:01:05 -0500] "DELETE /spring-utility/person/1/two HTTP/1.1" 400 1040

I can't find other directory named log in other place. Pls indicate me where I should find the complete error stack trace.

My application has a Main class, when it is executed some customized information is shown in the Console view. In the same application meanwhile the web server is running, there is other Console view. I forget to share the following:

First:

Code (part) from the Main class:

restClient.createPerson();
		
person = null;
person = restClient.getPerson("1");
		
logger.info("{}", person.toString());
		
//restClient.deletePersonOne("1");
restClient.deletePersonTwo(person);
//restClient.deletePersonThree("1");

Second: the console view for the Web application

DEBUG .annotation.RequestMappingHandlerMapping: 246 - Looking up handler method for path /person/1
DEBUG .annotation.RequestMappingHandlerMapping: 251 - Returning handler method [public org.springframework.http.ResponseEntity<com.manuel.jordan.domain.Person> com.manuel.jordan.web.controller.rest.PersonRestController.getPerson(java.lang.Integer)]
DEBUG ctory.support.DefaultListableBeanFactory: 249 - Returning cached instance of singleton bean 'personRestController'
DEBUG gframework.web.servlet.DispatcherServlet: 925 - Last-Modified value for [/spring-utility/person/1] is: -1
INFO web.controller.rest.PersonRestController:  53 - PersonRestController  - getPerson - id: 1
DEBUG hod.annotation.HttpEntityMethodProcessor: 145 - Written [Person [id=1, firstName=Manuel, lastName=Jordan, alive=true, dateBirth=Mon Jul 06 00:00:00 PET 1981, dateRetirement=Fri Jul 06 00:00:00 PET 2040, dateDeath=null, age=33, weight=75.5, salary=1500.88]] as "application/json;charset=UTF-8" using [org.springframework.http.converter.json.MappingJacksonHttpMessageConverter@d1434be]
DEBUG gframework.web.servlet.DispatcherServlet:1012 - Null ModelAndView returned to DispatcherServlet with name 'dispatcher': assuming HandlerAdapter completed request handling
DEBUG gframework.web.servlet.DispatcherServlet: 991 - Successfully completed request
DEBUG gframework.web.servlet.DispatcherServlet: 838 - DispatcherServlet with name 'dispatcher' processing DELETE request for [/spring-utility/person/1/two]
DEBUG .annotation.RequestMappingHandlerMapping: 246 - Looking up handler method for path /person/1/two
DEBUG .annotation.RequestMappingHandlerMapping: 251 - Returning handler method [public org.springframework.http.ResponseEntity<java.lang.String> com.manuel.jordan.web.controller.rest.PersonRestController.deletePersonTwo(java.lang.Integer,com.manuel.jordan.domain.Person)]
DEBUG ctory.support.DefaultListableBeanFactory: 249 - Returning cached instance of singleton bean 'personRestController'
DEBUG ation.RequestResponseBodyMethodProcessor: 135 - Reading [class com.manuel.jordan.domain.Person] as "application/json;charset=UTF-8" using [org.springframework.http.converter.json.MappingJacksonHttpMessageConverter@d1434be]
DEBUG tation.ExceptionHandlerExceptionResolver: 134 - Resolving exception from handler [public org.springframework.http.ResponseEntity<java.lang.String> com.manuel.jordan.web.controller.rest.PersonRestController.deletePersonTwo(java.lang.Integer,com.manuel.jordan.domain.Person)]: org.springframework.http.converter.HttpMessageNotReadableException: Could not read JSON: No content to map to Object due to end of input; nested exception is java.io.EOFException: No content to map to Object due to end of input
DEBUG notation.ResponseStatusExceptionResolver: 134 - Resolving exception from handler [public org.springframework.http.ResponseEntity<java.lang.String> com.manuel.jordan.web.controller.rest.PersonRestController.deletePersonTwo(java.lang.Integer,com.manuel.jordan.domain.Person)]: org.springframework.http.converter.HttpMessageNotReadableException: Could not read JSON: No content to map to Object due to end of input; nested exception is java.io.EOFException: No content to map to Object due to end of input
DEBUG .support.DefaultHandlerExceptionResolver: 134 - Resolving exception from handler [public org.springframework.http.ResponseEntity<java.lang.String> com.manuel.jordan.web.controller.rest.PersonRestController.deletePersonTwo(java.lang.Integer,com.manuel.jordan.domain.Person)]: org.springframework.http.converter.HttpMessageNotReadableException: Could not read JSON: No content to map to Object due to end of input; nested exception is java.io.EOFException: No content to map to Object due to end of input
DEBUG gframework.web.servlet.DispatcherServlet:1012 - Null ModelAndView returned to DispatcherServlet with name 'dispatcher': assuming HandlerAdapter completed request handling
DEBUG gframework.web.servlet.DispatcherServlet: 991 - Successfully completed request

Wondered about the:

org.springframework.http.converter.HttpMessageNotReadableException: Could not read JSON: No content to map to Object due to end of input; nested exception is java.io.EOFException: No content to map to Object due to end of input

If for HttpMethod.PUT works fine and has the same structure and it is executed from the Main. the two following forms works without any problem

restClient.createPerson();
		
Person person = null;
person = restClient.getPerson("1");
		
person.setFirstName(person.getFirstName().toUpperCase());
person.setLastName(person.getLastName().toUpperCase());
restClient.updatePersonOne(person);
		
person = null;
person = restClient.getPerson("1");
		
person.setAge(100);
person.setWeight(77.66);
restClient.updatePersonTwo(person);
		
person = null;
person = restClient.getPerson("1");

BTW: The restClient.getPerson method prints very well each updated Person retrieved form the Web Server.

So why java.io.EOFException appears for the HttpMethod.DELETE and not for HttpMethod.UPDATE

Both are the same practically. Seems I am forgetting an innocent detail.

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Oct 22, 2014

Rossen Stoyanchev commented

Okay thanks Manuel Jordan, that's all I need for now.

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Oct 22, 2014

Juergen Hoeller commented

Let's see what we can do for 4.1.2 still... if easy enough.

Juergen

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Oct 22, 2014

Manuel Jordan commented

Thanks to you. Very powerful and flexible Spring Rest support.

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Nov 10, 2014

Rossen Stoyanchev commented

Appears HttpUrlConnection only started supporting HTTP DELETE with body in JDK 1.8 (https://bugs.openjdk.java.net/browse/JDK-7157360).

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Nov 10, 2014

Rossen Stoyanchev commented

I've made the following additional change related to DELETE with HttpUrlConnection.

Effectively doOutput is turned off if the actual buffered body array is empty. This should make the main use case of DELETE without a body work as before. However when there is a body, or when streaming is enabled (i.e. bufferRequestBody=false) and content is actually attempted to be streamed, an exception will still occur with JDK 1.6 and 1.7 in contrast to how it worked before where the body was silently ignored.

/cc Janne Valkealahti if you can please try once more with this fix.

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Nov 10, 2014

Janne Valkealahti commented

Thanks Rossen, just tried it and problem with xd-shell went away :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: web type: enhancement
Projects
None yet
Development

No branches or pull requests

2 participants