restTemplate postForEntity sometimes leads into 400 Error - java

In my Android app I try to make a POST via restTemplate.postForEntity() but the first time I try to post the data I get a 400 error. After my timetrigger sync-method try to post the data again it works and I get 200. I don't think it's a backend problem, because I did a couple requests via Swagger and Postman on the same interface and all of them worked without a problem.
This is the error I get:
POST request for "<url>" resulted in 400 (); invoking error handler
org.springframework.web.client.HttpClientErrorException: 400
This is what I see at the postForEntity() when I'm debugging:
'java.lang.NullPointerException' Cannot evaluate org.springframework.http.ResponseEntity.toString()
This is the code:
public ResponseEntity<ArrayList> postServiceData(List<BasicService> attributes) {
HttpStatus status = null;
ResponseEntity<ArrayList> chargerServiceResponse = new ResponseEntity<ArrayList>(status);
try {
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
HttpEntity<?> entity = RestServiceUtils.getHttpEntity(attributes, context);
chargerServiceResponse = restTemplate.postForEntity(url, entity, ArrayList.class);
SynchronisationStorage synchronisationStorage = new SynchronisationStorage(context);
synchronisationStorage.updateLastSynchronisationDate();
loggingStorageDbHelper.logSuccess(R.string.logging_save_send_charger_service_to_backend, 1000);
} catch (HttpClientErrorException e) {
/* do stuff*/
}
}
To set the body and token:
#NonNull
public static HttpEntity<?> getHttpEntity(List attributes, Context context) {
HttpHeaders headers = new HttpHeaders();
try {
headers.set(ServiceAppConstants.HEADER_ACCEPT, ServiceAppConstants.MEDIATYPE_JSON);
UserStorage userStorage = new UserStorage(context);
String token = userStorage.getJsonWebToken();
headers.set(ServiceAppConstants.HEADER_SECURITY_TOKEN, token);
}catch (Exception ex){
Log.e(RestServiceUtils.class.getName(), "Exception ocurred while trying to set token to header", ex);
}
return new HttpEntity<Object>(attributes, headers);
}

Related

How to use CloudHealth API (provided by vmware) for fetching reports of client or tenant in spring boot application?

I want to implement CloudHealth API in my Spring Boot application. I want to fetch report of particular client. I have a dropdown where logged in user select reports and that report will be directly fetched from CloudHealth platform. I want to do that thing in my application. I want to generate JSON response of custom report. I followed API documentation available at https://apidocs.cloudhealthtech.com/#reporting_data-for-custom-report
but I am getting 404 Not Found: "{"error":"Record with id not found."}"
This is the code written in my service class:
public String getCustomReportData(String reportId) {
ResponseEntity<String> responseEntity = null;
String response = null;
try {
final String uri = "https://chapi.cloudhealthtech.com/olap_reports/custom/"+reportId;
RestTemplate restTemplate = new RestTemplate();
HttpHeaders header = new HttpHeaders();
header.set(HttpHeaders.AUTHORIZATION, "Bearer my-api-key");
header.set(HttpHeaders.ACCEPT,"application/json");
HttpEntity<String> requestEntity = new HttpEntity<String>("body",header);
responseEntity = restTemplate.exchange(uri, HttpMethod.GET, requestEntity, String.class);
response = responseEntity.getBody();
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
return response;
}
This is main endpoint in my restcontoller:
#RequestMapping(value = {"/custom_report/{report_id}"}, method = {RequestMethod.GET, RequestMethod.POST}, produces = {MediaType.APPLICATION_JSON_VALUE})
public ResponseEntity<Object> getCustomCloudHealthReports(HttpServletRequest request,#PathVariable("report_id") String reportId){
try {
String response = standardReportService.getCustomReportData(reportId);
return new ResponseEntity<Object>(response, HttpStatus.OK);
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
return new ResponseEntity<Object>("Please try again later", HttpStatus.INTERNAL_SERVER_ERROR);
}
}

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.

Response status code showing 200 for gateway error (504)

I have a REST endpoint which call another API which take a while to process and returning 504 error when I verify through Rest client (Insomnia). But in my service I see this transaction as success 200 not 504.
Below is how my code snippet:
public ResponseEntity<Customer> processResponse(Customer customer, String restUri) {
ResponseEntity<Customer> response;
String customerJson = null;
try {
RestTemplate restTemplate = restTemplateBuilder.basicAuthorization(userName, password).build();
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<Customer> entity = new HttpEntity<>(customer, headers);
CustomerJson = jacksonUtil.toJSON(customer);
response = restTemplate.exchange(restUri, HttpMethod.PUT, entity, Customer.class);
if (response.getStatusCode().is2xxSuccessful()) {
logger.info("Return success from the server");
} else {
logger.error("Error while getting the response from the server");
}
} catch (Exception ex) {
ex.printStackTrace();
throw ex;
}
return response;
}
What am I missing here? Why its not executing the else block?
Thanks in advance.
Your method seems to be returning null response in case of error. Can you check if you have done any handling in caller and passing 200 from controller layer itself.

415 Unsupported Media Type while sending json file over REST Template

I am trying to send a json file over REST Template. When I send it via POST man as MULTIPART_FORM_DATA, it works fine. The name I am supposed to give is specific (lets say aaa). Attached screenshot of POSTMAN. But when I try same in code as specified in another stackoverflow post, I get 415 Unsupported Media Type error as
org.springframework.web.client.HttpClientErrorException: 415 Unsupported Media Type
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:91) ~[spring-web-4.1.9.RELEASE.jar:4.1.9.RELEASE]
at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:616) ~[spring-web-4.1.9.RELEASE.jar:4.1.9.RELEASE]
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:572) ~[spring-web-4.1.9.RELEASE.jar:4.1.9.RELEASE]
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:532) ~[spring-web-4.1.9.RELEASE.jar:4.1.9.RELEASE]
at org.springframework.web.client.RestTemplate.postForObject(RestTemplate.java:332) ~[spring-web-4.1.9.RELEASE.jar:4.1.9.RELEASE]
at
Please do not mark it as duplicate as the specified answer did not work for me. Not sharing code as my code is exactly same as this except
requestParamerterMap.add("attachment", resource);
where as my code is
requestParamerterMap.add("aaa", resource);
After debugging it from the server side, looks like request is reaching out to server. I was able to see below error in the server side:
[{error=Unsupported Media Type, exception=org.springframework.web.HttpMediaTypeNotSupportedException, message=Content type 'application/octet-stream' not supported, status=415, timestamp=1532557180124}] as "application/json" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter#74d4827a]
So, from the server side logs, I am not sure where the content type is getting added as application/octet-stream as I have set the content type as
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
Below is the code from server controller. Server side code uses Spring boot.
#RequestMapping(method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE,consumes = {"multipart/form-data"})
#ResponseBody
public MyResponse uploadPhoto(#RequestPart(value = "aaa", required = false) Optional<MyRequest> myRequest,
#RequestPart(value = "file", required = false) Optional<MultipartFile> file,
HttpServletRequest request) {
//some logic
return myResponse;
}
The server code has an interceptor where I can see my request has content type as multipart/form-data. It does not reach to RestController
When I debugged the server side code in 2 cases:
POSTMAN request
client code request
One thing I figured out that file iteam has content type as application/json when I post from POSTMAN and the content type was application/octet-stream when the request goes from client side code.
In my client side code, I am creating JSONObject as
JSONObject json = new JSONObject();
json.append("myKey", "myValue");
and convert it to byte array as
json.toString().getBytes("UTF-8")
then I have followed this . The difference in my code is, I am sending my JSONObject as byte stream as I can not create file (performance issues).
And I cant not send JSONObject as string as server is expecting multipart-form-data for both file and aaa
I have created the restTemplate as
public RestTemplate myRestTemplate() {
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setReadTimeout(HTTP_CLIENT_TIMEOUT);
requestFactory.setConnectTimeout(HTTP_CLIENT_TIMEOUT);
RestTemplate restTemplate = new RestTemplate(requestFactory);
List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
messageConverters.add(new FormHttpMessageConverter());
messageConverters.add(new StringHttpMessageConverter());
restTemplate.setMessageConverters(messageConverters);
return restTemplate;
Here is the client side code which calls the service:
public Optional<JSONObject> callService(byte[] multipartFile) {
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
InputStream stream = new ByteArrayInputStream(multipartFile);
MultipartByteArrayResource resource = new MultipartByteArrayResource(multipartFile,fileName);
body.add("aaa", resource);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
try {
response = restTemplate.postForObject(url, requestEntity , String.class);
} catch (Exception exception) {
LOG.error("Error", exception);
return Optional.empty();
}
}
public class MultipartInputStreamFileResource extends InputStreamResource {
private final String filename;
MultipartInputStreamFileResource(InputStream inputStream, String filename) {
super(inputStream);
this.filename = filename;
}
#Override
public String getFilename() {
return this.filename;
}
#Override
public long contentLength() throws IOException {
return -1; // we do not want to generally read the whole stream into memory ...
}
}
And same code works when I send file (note file and aaa are two different things though both are multipart/form-data in server side. file is just a file of any time (image/text/pdf) but aaa is json data file)
After debugging little bit more, what I observed is server side controller is expecting the file content to be json as Jackson try to deserialize that json to MyRequest object. When I send post from POSTMAN, it has the json content so working as expected but from the client side code, the content is byteArray, and its not getting deserialize to MyRequest object. Not sure how to fix this
Finally I solved this issue. As mentioned in question, having different content type of multipart file while sending request from POSTMAN vs code is where I began with. I will explain in details if anyone has any questions.
public Optional<JSONObject> save(byte[] multipartFile, String fileName) {
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
Resource content = new MultipartByteArrayResource(multipartFile , fileName);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<Resource> requestEntityBody = new HttpEntity<Resource>(content, headers);
body.add("aaa", requestEntityBody);
String result = "";
JSONParser parser = new JSONParser();
JSONObject json = null;
HttpHeaders requestHeaders = new HttpHeaders();
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, requestHeaders);
ResponseEntity<String> response = null;
try {
RestTemplate restTemplate = customizeRestTemplate(); //I have defined this in different config file in my actual code
response = restTemplate.exchange(url , HttpMethod.POST , requestEntity , String.class);
result = (response != null && response.getBody() != null) ? response.getBody().toString() : result;
json = (JSONObject) parser.parse(result);
LOG.info( "Response:", response );
} catch (Exception exception) {
LOG.error("Error , exception);
return Optional.empty();
}
return Optional.ofNullable(json);
}
public class MultipartByteArrayResource extends ByteArrayResource{
private String fileName;
public MultipartByteArrayResource(byte[] byteArray , String filename) {
super(byteArray);
this.fileName = filename;
}
public String getFilename() {
return fileName;
}
public void setFilename(String fileName) {
this.fileName= fileName;
}
}
public RestTemplate customizeRestTemplate() {
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setReadTimeout(10000);
requestFactory.setConnectTimeout(10000);
RestTemplate restTemplate = new RestTemplate(requestFactory);
List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
messageConverters.add(new FormHttpMessageConverter());
messageConverters.add(new StringHttpMessageConverter());
restTemplate.setMessageConverters(messageConverters);
return restTemplate;
}
}
The server-side exception is produced by org.springframework.http.converter.json.MappingJackson2HttpMessageConverter. Jackson is a JSON library and MessageConverter are used by Spring to format requests and responses.
Can it be that the client sends an "Accept: application/octet-stream" while the server has a #Produces(APPLICATION_JSON) annotation? That would mean that the server processes the request and only has problems sending the response. You could add some log.info() statements in the server to verify this.

Receive the HTTP status after a request with Spring MVC

i'm sending data to a server and i want to receive the HTTP response status in order to check this status and provide the appropriate view
#RequestMapping(method = RequestMethod.POST)
public String Login(#ModelAttribute("Attribute") Login login, Model model,HttpServletRequest request) {
// Prepare acceptable media type
ArrayList<MediaType> acceptableMediaTypes = new ArrayList<MediaType>();
acceptableMediaTypes.add(MediaType.APPLICATION_XML);
// Prepare header
HttpHeaders headers = new HttpHeaders();
headers.setAccept(acceptableMediaTypes);
HttpEntity<Login> entity = new HttpEntity<Login>(login, headers);
// Send the request as POST
try {
ResponseEntity<Login> result = restTemplate.exchange("http://www.../user/login/",
HttpMethod.POST, entity, Login.class);
} catch (Exception e) {
}
//here i want to check the received status
if(status=="OK"){
return "login"
}
else
return "redirect:/home";
}
What's wrong with:
HttpStatus status = result.getStatusCode();
if(status == HttpStatus.OK)
See: ResponseEntity JavaDoc.
BTW you should not compare strings using == operator like here:
status=="OK"
Instead use the following idiom:
"OK".equals(status)
Also method names in Java tend to start with lower case.
The ResponseEntity object contains the HTTP status code.
// Prepare acceptable media type
ArrayList<MediaType> acceptableMediaTypes = new ArrayList<MediaType>();
acceptableMediaTypes.add(MediaType.APPLICATION_XML);
// Prepare header
HttpHeaders headers = new HttpHeaders();
headers.setAccept(acceptableMediaTypes);
HttpEntity<Login> entity = new HttpEntity<Login>(login, headers);
// Create status variable outside of try-catch block
HttpStatus statusCode = null;
// Send the request as POST
try {
ResponseEntity<Login> result = restTemplate.exchange("http://www.../user/login/",
HttpMethod.POST, entity, Login.class);
// Retrieve status code from ResponseEntity
statusCode = result.getStatusCode();
} catch (Exception e) {
}
// Check if status code is OK
if (statusCode == HttpStatus.OK) {
return "login"
}
else
return "redirect:/home";

Categories