I have the following functionality to implement:
The method makes a call to an external API with POST (create operation) request. If the creation is valid, the next step is to create a bucket in S3. If the first step fails, whole method should thrown an exception.
What I came up with:
#PostMapping
public ResponseEntity<?> foo(String name) {
String requestEntity = //....
try {
ResponseEntity resp =
new RestTemplate().exchange("http://localhost:8080/rest/someservice",
HttpMethod.POST, requestEntity, String.class);
if (resp.getStatusCode().is2xxSuccessful()) {
myBucket.makeBucket(requestEntity);
return new ResponseEntity<>(/*Some success message*/);
} else return new ResponseEntity<>(/*Some error message*/);
} catch (RestClientException ex) {
return new ResponseEntity<>(/*Some error message*/);
}
}
Is it a valid solution or perhaps I should do it differently ie. with CompletableFuture?
Related
I'm using RestTemplate client in Java and Spring Boot project and when I receive a response body from the server I have this code:
private RestTemplate oauth2RestTemplate;
private ParameterizedTypeReference<List<Employee>> parameterizedTypeReference;
....
ResponseEntity<List<Employee>> rtGetResponse = oauth2RestTemplate.exchange(uriComponents.toUri(), HttpMethod.GET, httpEntity, parameterizedTypeReference);
So in this case I will receive a list of employee.
Now I want to use this method but the response body from the server is void, and I want to use exchange method but I don't know what to use instead of parameterizedTypeReference because the response is void. So I dont have a response body, I want only to catch the exception.
Is ok to do something like that?
try {
oauth2RestTemplate.exchange(uriComponents.toUri(), HttpMethod.GET, httpEntity, null);
} catch (HttpStatusCodeException e) {
switch (e.getStatusCode()) {
case BAD_REQUEST:
throw new BadRequestException(e);
case NOT_FOUND:
throw new NotFoundException(e);
...
}
}
You can use Void class
try {
oauth2RestTemplate.exchange(uriComponents.toUri(), HttpMethod.GET, httpEntity, Void.class);
} catch (HttpStatusCodeException e) {
switch (e.getStatusCode()) {
case BAD_REQUEST:
throw new BadRequestException(e);
case NOT_FOUND:
throw new NotFoundException(e);
...
}
}
I am new to spring and I am working on a fairly simple REST API.
I am getting a request without any data, and I need to send back some info. What would be the better way of doing so?
Entity
#RequestMapping(value = "/ping", consumes = "application/json", method = RequestMethod.GET)
public ResponseEntity<?> checkServerStatus() throws Exception {
Ping ping = new Ping();
ping.setStatus("alive");
ping.setVersion("v1");
try {
return ResponseEntity.ok().body(ping);
} catch (Exception e) {
throw new Exception("Service is not reachable at the moment", e);
}
}
OR
Response
#RequestMapping(value = "/ping2", consumes = "application/json", method = RequestMethod.GET)
public ResponseEntity<?> checkServerStatus2() throws Exception {
try {
return ResponseEntity.ok(new PingResponse("alive", "v1"));
} catch (Exception e) {
throw new Exception("Service is not reachable at the moment", e);
}
}
I figured no need to show PingResponse and Ping. They are typical get/set classes.
If both ways aren't ideal then maybe there is a better way of doing so?
The only difference is that you have your object in the try block or not. And that PingResponse could be an immutable object (no setters).
Entity/Response? they are both Objects.
So there is no difference worth taking notice in your provided code examples.
From your example it seems that, you just need to let the caller know that your service is up. Why are you creating an object? You can just notify the caller.
return new ResponseEntity<>("success", HttpStatus.OK);
you can just return the object from back end
#GetMapping("/ping")
public Ping checkServerStatus(){
Ping ping = new Ping();
ping.setStatus("alive");
ping.setVersion("v1");
return ping;
}
So I have a Spring RestController and one of my endpoints is used to perform operations on a generic typed object passed into my RequestBody as so:
#PostMapping("/endpoint")
public <T extends Comparable<T>> ResponseEntity<Integer> balancingPost(#RequestBody MyCustomObject<T> mco)
So after a lot of searching it doesn't seem this can be done without explicitly stating the type at some point. However as it stands my controller has no way of knowing the type (the program calling the POST does though). So how should I handle this? Is there a way to post my Class of T as well and somehow map it?
Try following
public ResponseEntity<?> balancingPost(#RequestBody MyCustomObject<T> mco) {
ResponseEntity<?> response = null;
try {
/*Some condition*/
if (!auth.equals(authCode)) {
response = new ResponseEntity<>("Unauthorized", HttpStatus.UNAUTHORIZED);
} else {
MyModel model = service.getModel();
response = new ResponseEntity<>(model, HttpStatus.OK);
}
} catch (Exception ex) {
response = new ResponseEntity<>(ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
ex.printStackTrace();
}
return response;
}
The methods of RestTemplate such as postForEntity() throw RestClientException. I would like to extract the HTTP status code and response body from that exception object in the catch block. How can I do that?
Instead of catching RestClientException, catch the special HttpClientErrorException.
Here's an example:
try {
Link dataCenterLink = serviceInstance.getLink("dataCenter");
String dataCenterUrl = dataCenterLink.getHref();
DataCenterResource dataCenter =
restTemplate.getForObject(dataCenterUrl, DataCenterResource.class);
serviceInstance.setDataCenter(dataCenter);
} catch (HttpClientErrorException e) {
HttpStatus status = e.getStatusCode();
if (status != HttpStatus.NOT_FOUND) { throw e; }
}
HttpClientErrorException provides getStatusCode and getResponseBodyAsByteArray to get the status code and body, respectively.
Catch RestClientResponseException instead. It's more generic.
From the docs: Common base class for exceptions that contain actual HTTP response data.
In some cases, HttpClientErrorException is not thrown. For example the following method restTemplate.exchange call:
ResponseEntity<Employee[]> employees = restTemplate.exchange(url, HttpMethod.GET, entity, Employee[].class);
Gets the http body and marshalls it to an Entity. If remote resource returns a rare error, internal marshall does not work and just a RestClientException is thrown.
restTemplate.setErrorHandler
In this case or if you want to handle any error in restTemplate operations, you could use setErrorHandler. This method receives a basic ResponseErrorHandler with helpful methods.
This method hasError allowed me to get the remote http body text and helped me to detect the error of the invocation or in the remote http remote resource:
restTemplate.setErrorHandler(new ResponseErrorHandler() {
#Override
public boolean hasError(ClientHttpResponse arg0) throws IOException {
System.out.println("StatusCode from remote http resource:"+arg0.getStatusCode());
System.out.println("RawStatusCode from remote http resource:"+arg0.getRawStatusCode());
System.out.println("StatusText from remote http resource:"+arg0.getStatusText());
String body = new BufferedReader(new InputStreamReader(arg0.getBody()))
.lines().collect(Collectors.joining("\n"));
System.out.println("Error body from remote http resource:"+body);
return false;
}
#Override
public void handleError(ClientHttpResponse arg0) throws IOException {
// do something
}
});
Also, you can manually evaluate the body or status and return true or false in order to flag as error or not.
private void sendActivity(StatsActivity statsActivity) throws InterruptedException
{
LibraryConnectorXapiEditView libraryConnectorXapiEditView = (LibraryConnectorXapiEditView) workerBundle.getConnector();
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
Statement statement = libraryConnectorConverter.convertActivityToStatement(statsActivity, workerBundle);
HttpEntity<Statement> request = new HttpEntity<>(statement, headers);
try
{
String lrsEndPoint = libraryConnectorXapiEditView.getLrsEndPoint() + "/statements";
ResponseEntity<String> response = restTemplate.exchange(lrsEndPoint, HttpMethod.POST, request, String.class);
ocnCompletionEventDao.save(this.convertToOcnCompletionEvent(statsActivity, response.getBody(), response.getStatusCodeValue()));
}
catch (HttpClientErrorException ex)
{
ocnCompletionEventDao.save(this.convertToOcnCompletionEvent(statsActivity, ex.getResponseBodyAsString(), ex.getStatusCode().value()));
checkResponse(ex, libraryConnectorXapiEditView);
if(failedAttempts<3)
{
sendActivity(statsActivity);
failedAttempts++;
}
}
}
private void checkResponse(HttpClientErrorException ex, LibraryConnectorXapiEditView libraryConnectorXapiEditView) throws InterruptedException
{
int statusCode = ex.getStatusCode().value();
int retryAfterSeconds = retryAfter(ex.getResponseHeaders());
switch (statusCode)
{
case 401:
headers = xApiAuthorizationUtils.getHeaders(libraryConnectorXapiEditView);
case 429:
if(retryAfterSeconds!=0)
Thread.sleep(retryAfterSeconds);
case 422:
failedAttempts=3;
}
}
The methods of RestTemplate such as postForEntity() throw RestClientException. I would like to extract the HTTP status code and response body from that exception object in the catch block. How can I do that?
Instead of catching RestClientException, catch the special HttpClientErrorException.
Here's an example:
try {
Link dataCenterLink = serviceInstance.getLink("dataCenter");
String dataCenterUrl = dataCenterLink.getHref();
DataCenterResource dataCenter =
restTemplate.getForObject(dataCenterUrl, DataCenterResource.class);
serviceInstance.setDataCenter(dataCenter);
} catch (HttpClientErrorException e) {
HttpStatus status = e.getStatusCode();
if (status != HttpStatus.NOT_FOUND) { throw e; }
}
HttpClientErrorException provides getStatusCode and getResponseBodyAsByteArray to get the status code and body, respectively.
Catch RestClientResponseException instead. It's more generic.
From the docs: Common base class for exceptions that contain actual HTTP response data.
In some cases, HttpClientErrorException is not thrown. For example the following method restTemplate.exchange call:
ResponseEntity<Employee[]> employees = restTemplate.exchange(url, HttpMethod.GET, entity, Employee[].class);
Gets the http body and marshalls it to an Entity. If remote resource returns a rare error, internal marshall does not work and just a RestClientException is thrown.
restTemplate.setErrorHandler
In this case or if you want to handle any error in restTemplate operations, you could use setErrorHandler. This method receives a basic ResponseErrorHandler with helpful methods.
This method hasError allowed me to get the remote http body text and helped me to detect the error of the invocation or in the remote http remote resource:
restTemplate.setErrorHandler(new ResponseErrorHandler() {
#Override
public boolean hasError(ClientHttpResponse arg0) throws IOException {
System.out.println("StatusCode from remote http resource:"+arg0.getStatusCode());
System.out.println("RawStatusCode from remote http resource:"+arg0.getRawStatusCode());
System.out.println("StatusText from remote http resource:"+arg0.getStatusText());
String body = new BufferedReader(new InputStreamReader(arg0.getBody()))
.lines().collect(Collectors.joining("\n"));
System.out.println("Error body from remote http resource:"+body);
return false;
}
#Override
public void handleError(ClientHttpResponse arg0) throws IOException {
// do something
}
});
Also, you can manually evaluate the body or status and return true or false in order to flag as error or not.
private void sendActivity(StatsActivity statsActivity) throws InterruptedException
{
LibraryConnectorXapiEditView libraryConnectorXapiEditView = (LibraryConnectorXapiEditView) workerBundle.getConnector();
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
Statement statement = libraryConnectorConverter.convertActivityToStatement(statsActivity, workerBundle);
HttpEntity<Statement> request = new HttpEntity<>(statement, headers);
try
{
String lrsEndPoint = libraryConnectorXapiEditView.getLrsEndPoint() + "/statements";
ResponseEntity<String> response = restTemplate.exchange(lrsEndPoint, HttpMethod.POST, request, String.class);
ocnCompletionEventDao.save(this.convertToOcnCompletionEvent(statsActivity, response.getBody(), response.getStatusCodeValue()));
}
catch (HttpClientErrorException ex)
{
ocnCompletionEventDao.save(this.convertToOcnCompletionEvent(statsActivity, ex.getResponseBodyAsString(), ex.getStatusCode().value()));
checkResponse(ex, libraryConnectorXapiEditView);
if(failedAttempts<3)
{
sendActivity(statsActivity);
failedAttempts++;
}
}
}
private void checkResponse(HttpClientErrorException ex, LibraryConnectorXapiEditView libraryConnectorXapiEditView) throws InterruptedException
{
int statusCode = ex.getStatusCode().value();
int retryAfterSeconds = retryAfter(ex.getResponseHeaders());
switch (statusCode)
{
case 401:
headers = xApiAuthorizationUtils.getHeaders(libraryConnectorXapiEditView);
case 429:
if(retryAfterSeconds!=0)
Thread.sleep(retryAfterSeconds);
case 422:
failedAttempts=3;
}
}