restTemplate delete with body - java

I am trying to do DELETE with request body but I keep getting 400 (bad request) error. When I do it in the swagger/postman, it is successfully deleting the record. But from the Java code I can't do that
The external API is designed in a way that it needs body along with URL. It can't be changed. please let me know how can I delete that entry with request body
public Person delete(Person person, String url, Map<String, String> uriVariables) throws JsonProcessingException {
RestTemplate restTemplate = new RestTemplate();
CustomObjectMapper mapper = new CustomObjectMapper();
HttpEntity<Person> requestEntity = new HttpEntity<Person>(person);
try {
ResponseEntity<Person> responseEntity = restTemplate.exchange(url, HttpMethod.DELETE, requestEntity, Person.class, uriVariables);
return responseEntity.getBody();
} catch (RestClientException e) {
System.out.println(mapper.writeValueAsString(person));
throw e;
}
}
when it goes to exception, I will get the JSON request in JSON format and the same works fine in Swagger/postman
I did some googling and found that restTemplate have problem deleting when there is request body. this article wasn't helpful https://jira.spring.io/browse/SPR-12361 is there any way to get it work

Another way to fix this is to use restTemplate.exchange, here's an example:
try {
String jsonPayload = GSON.toJson(request);
HttpEntity<String> entity = new HttpEntity<String>(jsonPayload.toString(),headers());
ResponseEntity resp = restTemplate.exchange(url, HttpMethod.DELETE, entity, String.class);
} catch (HttpClientErrorException e) {
/* Handle error */
}
The nice thing about this solution is that you can use it with all HttpMethod types.

Issue exists for Spring version 4.0.x or earlier.
In later version it has been fixed.
This might be a late answer, but in one of my project I solved this issue via a custom ClientHttpRequestFactory to RestTemplate
If no factory is provided to RestTemplate, it uses default implementation SimpleClientHttpRequestFactory
In SimpleClientHttpRequestFactory class, DELETE method is not allowed with request body.
if ("PUT".equals(httpMethod) || "POST".equals(httpMethod) ||
"PATCH".equals(httpMethod)) {
connection.setDoOutput(true);
}
else {
connection.setDoOutput(false);
}
Just write your own implementation as
import java.io.IOException;
import java.net.HttpURLConnection;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
public class CustomClientHttpRequestFactory extends SimpleClientHttpRequestFactory {
#Override
protected void prepareConnection(HttpURLConnection connection,
String httpMethod) throws IOException {
super.prepareConnection(connection, httpMethod);
if("DELETE".equals(httpMethod)) {
connection.setDoOutput(true);
}
}
}
After that create your RestTemplate object as
RestTemplate template = new RestTemplate(
new CustomClientHttpRequestFactory());
Fix in later versions of (4.1.x or above) SimpleClientHttpRequestFactory class
if ("POST".equals(httpMethod) || "PUT".equals(httpMethod) ||
"PATCH".equals(httpMethod) || "DELETE".equals(httpMethod)) {
connection.setDoOutput(true);
}
else {
connection.setDoOutput(false);
}

Related

Java Supplier<> get origins information

I Use the Supplier in my code to call restTemplate and make the custom Message when have exception..
But, im my message, i need get information by my requestCall, But when i cast the request the java thow error
...
My code:
public void execute() {
HttpHeaders headers = buildDefaultHeaders();
UriBuilder uri = UriBuilder.fromUri(wdd3dGatewayEndpoint + API_URL);
HttpEntity request = new HttpEntity(headers);
this.executeRequest(() -> restTemplate.exchange(uri.build(), HttpMethod.DELETE, request, Void.class));
}
My Supplier
protected ResponseEntity executeRequest(Supplier<ResponseEntity> request) {
try {
ResponseEntity response = request.get();
updateSessionToken(response);
return response;
} catch (HttpClientErrorException | HttpServerErrorException e) {
String msg = "WDD3D-Error in service communication<br>" + e.getResponseBodyAsString();
throw new MaestroException(msg);
}
}
Now, i try cast to get URL...
protected ResponseEntity executeRequest(Supplier<ResponseEntity> request) {
try {
ResponseEntity response = request.get();
updateSessionToken(response);
return response;
} catch (HttpClientErrorException | HttpServerErrorException e) {
//THROW EXEPTION HERE... PLEASE HELP...
RequestEntity requestEntity = (RequestEntity) request;
String url = requestEntity.getUrl().toString();
String msg = "WDD3D-Error in service communication<br>" + e.getResponseBodyAsString();
throw new MaestroException(msg);
}
}]
You should use the get() method of the Supplier, see more in the docs.
RequestEntity requestEntity = (RequestEntity) request;
You are trying to cast a Supplier<ResponseEntity> to a RequestEntity.
These are two very different classes and such a cast will never work.
Maybe you want to call request.get() and get the URL from the ResponseEntity that you have.
Tell me if it works for you in the comments or we need to debug further ?
The only thing you are trying to get from the RequestEntity is the URL, which you can't get from the Supplier<ResponseEntity> since it is not a RequestEntity, so why not just pass the URL as another parameter to executeRequest? Then it would have the additional information it needs to log the error.

How we can pass the response if ResponseEntity got failed(400 Error)

I am hitting one API using RestTemplate exchange method, Here I am getting responseEntity of ClientResponse Type. If we have any Bad request in first line of code, I'll get 400 and cursor will go to the catch and throwing Error. So remaining code(For setting a response Data) is not executing .Instead of this I want to set the response Data and I want to set status code also and want to execute remain code. How we can do it, Do we need to use Flag variable ??
ResponseEntity<ClientResponse> responseEntity = this.getRestTemplate().exchange(API_URL,
HttpMethod.POST, entity, ClientResponse.class);
response.setResponseEntity(responseEntity);
response.setValue(inputRequest.getValue));
response.setEndTime(LocalDateTime.now());
response.setRequestPayload(gson.toJson(inputRequest));
response.setHttpMethod(HttpMethod.POST);
response.setRequestHeaders(entity.getHeaders().toSingleValueMap());
} catch (Exception e) {
LOG.error("error occurred in service" + e.getMessage());
}
return response;
You can use restTemplate error handler for that. You can parse the error response returned from the rest template like 404 or some other error.
You can use those status code and error response to rest the response as per your need.
For that you need to define a class by implementing ResponseErrorHander.
public class ServiceResponseErrorHandler implements ResponseErrorHandler {
private List<HttpMessageConverter<?>> messageConverters;
#Override
public boolean hasError(ClientHttpResponse response) throws IOException {
return (response.getStatusCode().is4xxClientError() ||
response.getStatusCode().is5xxServerError());
}
#Override
public void handleError(ClientHttpResponse response) throws IOException {
#SuppressWarnings({ "unchecked", "rawtypes" })
HttpMessageConverterExtractor<ServiceErrorResponse> errorMessageExtractor =
new HttpMessageConverterExtractor(ServiceErrorResponse.class, messageConverters);
ServiceErrorResponse errorObject = errorMessageExtractor.extractData(response);
throw new ResponseEntityErrorException(
ResponseEntity.status(response.getRawStatusCode())
.headers(response.getHeaders())
.body(errorObject)
);
}
public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
this.messageConverters = messageConverters;
}
}
And use it in your rest template bean like this.
RestTemplateResponseErrorHandler errorHandler = new
RestTemplateResponseErrorHandler();
//pass the messageConverters to errror handler and let it convert json to object
errorHandler.setMessageConverters(restTemplate.getMessageConverters());
restTemplate.setErrorHandler(errorHandler);

Response Entity is null despite Error-404 JSON returned by a server

I am using Spring REST to handle my requests.
Here is my code sample:
RestTemplate restTemplate = new RestTemplate();
HttpEntity entity = new HttpEntity(headers);
ResponseEntity<String> responseEntity = null;
try {
responseEntity = restTemplate.exchange(apiAddress + "action?uniqueId=" + id, HttpMethod.GET, entity, String.class);
}catch (RestClientException ex) {
System.out.println(responseEntity.toString());
String errorMessage = ex.getMessage();
}
Everything is OK when I have got 200 status and JSON with returned values.
The problem is with, for example, 404 JSONs.
During the debugging I have figured out that when 404 occurs my responseEntity is still null so I am unable to get the error code. Moreover I am unable to get JSON reply from the server which I know that it being send.
I tried HTTP Requester which works fine with 200 responses - giving me requested data and with 404 responses - giving me a JSON with error description.
Best Regards,
Karol
I have managed to figure out what is going on.
I Hope that it might help someone else:
I have implemented ResponseErrorHandler :
public class VariousErrorsResponseHandler implements ResponseErrorHandler {
#Override
public boolean hasError(ClientHttpResponse clientHttpResponse) throws IOException {
if(clientHttpResponse.getStatusCode() == HttpStatus.NOT_FOUND || clientHttpResponse.getStatusCode() == HttpStatus.UNAUTHORIZED) return false;
else return true;
}
#Override
public void handleError(ClientHttpResponse clientHttpResponse) throws IOException {
System.err.println(clientHttpResponse.getStatusCode() + "\n" + clientHttpResponse.getStatusText());
}
}
Then set it as restTemplate's error handler:
restTemplate.setErrorHandler(new VariousErrorsResponseHandler());
And done :)

Always get null response in ResponseEntity when using RestTemplate.postForEntity

I' m trying to send JSON request using Jackson library from my Android app to the web server but response is always null. I tested it just with the HttpRequest API and all works fine - I've got a response. But now I try to use Spring RestTemplate and I can't receive a result. Here is my code:
protected Void doInBackground(String... params) {
LinkedHashMap<String, Object> _map = new LinkedHashMap<String, Object>();
_map.put("login", "Fred");
_map.put("password", "pass");
ObjectMapper _mapper = new ObjectMapper ();
StringWriter _writer = new StringWriter();
try {
_mapper.writeValue(_writer,_map);
} catch (JsonGenerationException e) {
e.printStackTrace();
} catch (JsonMappingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
String _baseURL = "https...."//Address of the server;
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> _entity = new HttpEntity<String>(_writer.toString(),requestHeaders);
RestTemplate templ = new RestTemplate();
templ.setRequestFactory(new HttpComponentsClientHttpRequestFactory());
templ.getMessageConverters().add(new StringHttpMessageConverter());
ResponseEntity<String> _response = templ.postForEntity(_baseURL, _entity,String.class);
String _Test = _response.getBody();
So I always have null in _Test.
I suspect this is because of https protocol. Can RestTemplate work with https?
So what's wrong with that code. How to fix this?
Thanks in advance. I really need a help!
You have to set the responseType, otherwise the RestTemplate will throw away the body of your response. It needs the responseType to find the correct message converter. With a null responseType, the delegate below will be null...
if (delegate != null) {
T body = delegate.extractData(response);
return new ResponseEntity<T>(body, response.getHeaders(), response.getStatusCode());
}
else {
return new ResponseEntity<T>(response.getHeaders(), response.getStatusCode());
}
With the RestTemplate default constructor, Spring includes just about every converter except for RSS, XML and JSON, which depends on if Rome, JAXB or Jackson is on the classpath. I would set the responseType as String and run it with the debugger to see why it's not finding the correct converter. It's hard for me to say why without seeing the response and headers from the server.
Typo or are you connecting to an https port?
String _baseURL = "https...."//Address of the server;
I think you should monitor the port you are trying to connect to and see if there is a connection even established. One easy way I do that is to make a laptop an ad-hoc network and have an Android device connect to it and then, you should be able to monitor all traffic from your android device with a packet sniffer like wireshark.

Spring RestTemplate Behavior when handling responses with a status of NO_CONTENT

Okay, I have a class NamedSystems, that has as its only field a Set of NamedSystem.
I have a method to find NamedSystems by certain criteria. That's not really important. When it gets results, everything works fine. However, when it can't find anything, and thus returns a null (or empty -- I've tried both ways) set, I get problems. Let me explain.
I'm using the Spring RestTemplate class and I'm making a call like this in a unit test:
ResponseEntity<?> responseEntity = template.exchange(BASE_SERVICE_URL + "?
alias={aliasValue}&aliasAuthority={aliasAssigningAuthority}",
HttpMethod.GET, makeHttpEntity("xml"), NamedSystems.class,
alias1.getAlias(), alias1.getAuthority());
Now, since this would normally return a 200, but I want to return a 204, I have an interceptor in my service that determines if a ModelAndView is a NamedSystem and if its set is null. If so, I then the set the status code to NO_CONTENT (204).
When I run my junit test, I get this error:
org.springframework.web.client.RestClientException: Cannot extract response: no Content-Type found
Setting the status to NO_CONTENT seems to wipe the content-type field (which does make sense when I think about it). So why is it even looking at it?
Spring's HttpMessageConverterExtractor extractData method:
public T extractData(ClientHttpResponse response) throws IOException {
MediaType contentType = response.getHeaders().getContentType();
if (contentType == null) {
throw new RestClientException("Cannot extract response: no Content-Type found");
}
for (HttpMessageConverter messageConverter : messageConverters) {
if (messageConverter.canRead(responseType, contentType)) {
if (logger.isDebugEnabled()) {
logger.debug("Reading [" + responseType.getName() + "] as \"" + contentType
+"\" using [" + messageConverter + "]");
}
return (T) messageConverter.read(this.responseType, response);
}
}
throw new RestClientException(
"Could not extract response: no suitable HttpMessageConverter found for response type [" +
this.responseType.getName() + "] and content type [" + contentType + "]");
}
Going up the chain a bit to find out where that Extractor is set, I come to RestTemplate's exchange() method that I used in the test:
public <T> ResponseEntity<T> exchange(String url, HttpMethod method,
HttpEntity<?> requestEntity, Class<T> responseType, Object... uriVariables) throws RestClientException {
HttpEntityRequestCallback requestCallback = new HttpEntityRequestCallback(requestEntity, responseType);
ResponseEntityResponseExtractor<T> responseExtractor = new ResponseEntityResponseExtractor<T>(responseType);
return execute(url, method, requestCallback, responseExtractor, uriVariables);
}
So, it's trying to convert what amounts to nothing because of the supplied response type from the exchange call. If I change the responseType from NamedSystems.class to null, it works as expected. It doesn't try to convert anything. If I had tried to set the status code to 404, it also executes fine.
Am I misguided, or does this seem like a flaw in RestTemplate? Sure, I'm using a junit right now so I know what's going to happen, but if someone is using RestTemplate to call this and doesn't know the outcome of the service call, they would naturally have NamedSystems as a response type. However, if they tried a criteria search that came up with no elements, they'd have this nasty error.
Is there a way around this without overriding any RestTemplate stuff? Am I viewing this situation incorrectly? Please help as I'm a bit baffled.
One more way to solve this would be to make response entity as null as shown below.
ResponseEntity<?> response = restTemplate.exchange("http://localhost:8080/myapp/user/{userID}",
HttpMethod.DELETE,
requestEntity,
null,
userID);
If you still need response headers, try implementing the ResponseErrorHandler.
I believe you should probably look at the ResponseExtractor interface & call execute on the RestTemplate providing your implementation of the extractor. To me it looks like a common requirement to do this so have logged this:
https://jira.springsource.org/browse/SPR-8016
Here's one I prepared earlier:
private class MyResponseExtractor extends HttpMessageConverterExtractor<MyEntity> {
public MyResponseExtractor (Class<MyEntity> responseType,
List<HttpMessageConverter<?>> messageConverters) {
super(responseType, messageConverters);
}
#Override
public MyEntity extractData(ClientHttpResponse response) throws IOException {
MyEntity result;
if (response.getStatusCode() == HttpStatus.OK) {
result = super.extractData(response);
} else {
result = null;
}
return result;
}
}
I've tested this & it seems to do what I want.
To create the instance of the ResponseExtractor I call the constructor & pass the converters from a RestTemplate instance that's been injected;
E.g.
ResponseExtractor<MyEntity> responseExtractor =
new MyResponseExtractor(MyEntity.class, restTemplate.getMessageConverters());
Then the call is:
MyEntity responseAsEntity =
restTemplate.execute(urlToCall, HttpMethod.GET, null, responseExtractor);
Your mileage may vary. ;-)
Here's a simple solution where you can set the default Content-Type for use if it is missing in the response. The Content-Type is added to the response header before it is handed back off to the preconfigured ResponseExtractor for extraction.
public class CustomRestTemplate extends RestTemplate {
private MediaType defaultResponseContentType;
public CustomRestTemplate() {
super();
}
public CustomRestTemplate(ClientHttpRequestFactory requestFactory) {
super(requestFactory);
}
public void setDefaultResponseContentType(String defaultResponseContentType) {
this.defaultResponseContentType = MediaType.parseMediaType(defaultResponseContentType);
}
#Override
protected <T> T doExecute(URI url, HttpMethod method, RequestCallback requestCallback, final ResponseExtractor<T> responseExtractor)
throws RestClientException {
return super.doExecute(url, method, requestCallback, new ResponseExtractor<T>() {
public T extractData(ClientHttpResponse response) throws IOException {
if (response.getHeaders().getContentType() == null && defaultResponseContentType != null) {
response.getHeaders().setContentType(defaultResponseContentType);
}
return responseExtractor.extractData(response);
}
});
}
}
This should now be fixed in Spring 3.1 RC1.
https://jira.spring.io/browse/SPR-7911
Or you could extend RestTemplate and override doExecute(..) and check the response body.
For example here is what I implemented and works for us:
#Override
protected <T> T doExecute(final URI url, final HttpMethod method, final RequestCallback requestCallback, final ResponseExtractor<T> responseExtractor)
throws RestClientException
{
Assert.notNull(url, "'url' must not be null");
Assert.notNull(method, "'method' must not be null");
ClientHttpResponse response = null;
try
{
final ClientHttpRequest request = createRequest(url, method);
if (requestCallback != null)
{
requestCallback.doWithRequest(request);
}
response = request.execute();
if (!getErrorHandler().hasError(response))
{
logResponseStatus(method, url, response);
}
else
{
handleResponseError(method, url, response);
}
if ((response.getBody() == null) || (responseExtractor == null))
{
return null;
}
return responseExtractor.extractData(response);
}
catch (final IOException ex)
{
throw new ResourceAccessException("I/O error: " + ex.getMessage(), ex);
}
finally
{
if (response != null)
{
response.close();
}
}
}
I think you are right.
I'm having a similar problem.
I think we should be getting a ResponseEntity with a HttpStatus of NO_CONTENT and a null body.
I came along a workaround (not sure if it meets your case):
First define a custom interceptor class which implements ClientHttpRequestInterceptor. and check if response.getStatusCode() meets your case (my case is != HttpStatus.NOT_FOUND and response.getBody() length is 0), define a custom class (e.x. DefaultResponseForEmptyRestTemplateBody) which has a static method of type MockClientHttpResponse:
public class RequestResponseInterceptor implements ClientHttpRequestInterceptor {
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
ClientHttpResponse response = execution.execute(request, body);
if(response.getStatusCode()!=HttpStatus.NOT_FOUND && response.getBody().readAllBytes().length==0){
response = DefaultResponseForEmptyRestTemplateBody.getResponse(response.getStatusCode());
}
return response;
}
}
public static class DefaultResponseForEmptyRestTemplateBody {
MockClientHttpResponse response;
private static byte[] content = new byte[0];
public static MockClientHttpResponse getResponse(HttpStatus statusCode){
content = "response body is empty".getBytes();
return new MockClientHttpResponse(content, statusCode);
}
}
finally add this interceptor to your restTemplate object as below:
restTemplate.setInterceptors(Collections.singletonList(new RequestResponseLoggingInterceptor()));
and call your restTemplate.postForEntity:
ResponseEntity<String> response = this.restTemplate.postForEntity(baseUrl, requestParams,String.class);

Categories