Spring Boot custom receive responses with HTTP OPTIONS by throw exceptions - java

I am new to Spring Boot, and I am trying to test a connection using HTTP OPTIONS.
My design is that I have a Service class that contains the logics for the testing. I also have an API Controller class that implements the method from Service.
My currently understanding is that the controller can be used to respond back different HTTP statuses using exceptions.
This is the method I wrote inside the controller for this purpose:
#PostMapping(path = "/test")
public ResponseEntity<Void> testConnection(#RequestBody URL url) {
try {
ControllerService.testConnection(url);
return ResponseEntity.status(HttpStatus.NO_CONTENT).body(null);
} catch (CredentialsException e) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(null);
} catch (URLException | URISyntaxException e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
} catch (UnknownException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
}
}
The way exceptions are triggered and the method testConnection() are inside the service class:
public static void testConnection(URL url)
throws URISyntaxException, CredentialsException, URLException, UnknownException {
String authHeaderValue = "Basic " + Base64.getEncoder().encodeToString(("user" + ':' + "password").getBytes());
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set("Authorization", authHeaderValue);
RestTemplate rest = new RestTemplate();
final ResponseEntity<Object> optionsResponse = rest.exchange(url.toURI(), HttpMethod.OPTIONS, new HttpEntity<>(requestHeaders), Object.class);
int code = optionsResponse.getStatusCodeValue();
if (code == 403) {
throw new InvalidCredentialsException();
} else if (code == 404) {
throw new InvalidURLException();
} else if (code == 500) {
throw new UnknownErrorException();
} else if (code == 200){
String message = "Test connection successful";
LOGGER.info(message);
}
}
I have created those custom exception classes.
Is this the proper way to trigger the right HTTP response inside the controller method or does Spring Boot has some other design? If so, is my list of exceptions comprehensive enough or do I need to add more to the testConnection() method in the service class?

You can write ExceptionHandler for each of the Exception type, so you don't have to repeat the code or use try/ catch block at all. Just let your testConnection and other methods to throw the exception.
import org.springframework.web.bind.annotation.ExceptionHandler;
#ExceptionHandler(CredentialsException.class)
public void credentialsExceptionHandler(CredentialsException e, HttpServletResponse response) throws IOException {
response.sendError(HttpStatus.FORBIDDEN.value(), e.getMessage());
}
There are different ways to define and use the ExceptionHandler method. But conceptually same.

Related

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);

How to pass upstream error response to downstream service using RestTemplate

I am not able to handle Error Response from one microservice to another.
Suppose service A calls service B.
B returns :
{
"timestamp": "2020-04-18T13:02:30.543+0000",
"status": 404,
"error": "Not Found",
"message": "Cannot find product with productId = 1",
"path": "/products/quantity/1"
}
as body in ResponseEntity.
I have to fetch the same response message,status and error in A.
I am using RestTemplate to call B from A.
I tried
public class RestTemplateResponseErrorHandler implements ResponseErrorHandler {
#Override
public boolean hasError(ClientHttpResponse clientHttpResponse) throws IOException {
return (clientHttpResponse.getStatusCode().series() == CLIENT_ERROR ||
clientHttpResponse.getStatusCode().series() == SERVER_ERROR
);
}
#Override
public void handleError(ClientHttpResponse clientHttpResponse) throws IOException {
if(clientHttpResponse.getStatusCode().series() == CLIENT_ERROR) {
HttpStatus httpStatus = clientHttpResponse.getStatusCode();
}
else if(clientHttpResponse.getStatusCode().series() == SERVER_ERROR) {
throw new RecordNotFoundException("Record Not found");
}
}
But here I am only able to get ResponseCode and not the whole ResponseBodywhich contains error and message.
Your error handler should look like this:
public class RestTemplateResponseErrorHandler implements ResponseErrorHandler {
#Override
public boolean hasError(ClientHttpResponse clientHttpResponse) throws IOException {
return (clientHttpResponse.getStatusCode().series() == HttpStatus.Series.CLIENT_ERROR
|| clientHttpResponse.getStatusCode().series() == HttpStatus.Series.SERVER_ERROR);
}
#Override
public void handleError(ClientHttpResponse clientHttpResponse) throws IOException {
HttpStatus statusCode = clientHttpResponse.getStatusCode();
switch (statusCode.series()) {
// handle client errors. Throw HttpClientErrorException
// ex.getResponseBodyAsString();
case CLIENT_ERROR:
HttpClientErrorException ex = new HttpClientErrorException(statusCode,
clientHttpResponse.getStatusText(), clientHttpResponse.getHeaders(),
this.getResponseBody(clientHttpResponse), this.getCharset(clientHttpResponse));
throw ex;
case SERVER_ERROR:
// handle server errors, may be the same as client errors, by throwing
// HttpServerErrorException
break;
default:
// default behavior for other errors
throw new RestClientException("Some Exception message");
}
}
private Charset getCharset(ClientHttpResponse response) {
HttpHeaders headers = response.getHeaders();
MediaType contentType = headers.getContentType();
return contentType != null ? contentType.getCharset() : null;
}
private byte[] getResponseBody(ClientHttpResponse response) {
byte[] result = new byte[0];
try {
// this is org.springframework.util.FileCopyUtils class
result = FileCopyUtils.copyToByteArray(response.getBody());
} catch (IOException var3) {
// Handle I/O exception
}
return result;
}
}
Then catch an exception and get a response by getResponseBodyAsString() method.
But if you have not public REST service, you may, I think, return application logic errors with status code 200. Create a base class for all responses with fields: errorCode, errorMessage, errorCause (for example). Extends it by your REST response classes. And if everything ok - return a regular response with errorCode 0. But if you get an error while handle requests, return a response with some errorCode (not 0), and fill errorMessage and errorCause fields. This is not "Best practice", but sometimes may be comprehensive for you.

Continue after exception or Error in Rest Api call using OAuth2RestTemplate

i am making a rest call using Spring Oauth2RestTemplate. I am trying to catch any exception while trying to make a restAPI call and continue the flow of Exception.
Two ways i tried:
Way I(Using try catch).
public ResponseEntity<Object> getResponse(URI uri, HttpHeaders httpHeaders,
Object obj) {
ResponseEntity<Object> response = null;
try {
response = restTemplate.exchange(uri, HttpMethod.POST, new HttpEntity<>(obj, httpHeaders),
Object.class);
} catch (Exception serverEx) {
LOGGER.error("ERROR while calling API.Full Exception: ",serverEx);
response.getBody().setLink(object.getUrl());
}
return response;
}
Way II(Custom Handling).
public class RestTemplateResponseErrorHandler implements ResponseErrorHandler {
private static final Logger LOGGER = LogManager.getLogger(RestTemplateResponseErrorHandler.class);
#Override
public boolean hasError(ClientHttpResponse httpResponse) throws IOException {
return (httpResponse.getStatusCode().series() == Series.CLIENT_ERROR
|| httpResponse.getStatusCode().series() == Series.SERVER_ERROR);
}
#Override
public void handleError(ClientHttpResponse httpResponse) {
//Log The Error but contibue the flow
}
}
But neither way the execution gets stopped. I want to continue the flow of the execution. if the call fails i want to handle it and continue the flow. Can any one please suggest whats happening here?
Exception:
Caused by: java.io.IOException: Attempted read from closed stream.
at org.apache.http.impl.io.ContentLengthInputStream.read(ContentLengthInputStream.java:131)
at org.apache.http.conn.EofSensorInputStream.read(EofSensorInputStream.java:118)
at java.io.FilterInputStream.read(FilterInputStream.java:83)
at java.io.PushbackInputStream.read(PushbackInputStream.java:139)
at org.springframework.web.client.MessageBodyClientHttpResponseWrapper.hasEmptyMessageBody(MessageBodyClientHttpResponseWrapper.java:102)
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:82)
at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:932)
at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:916)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:663)
... 223 more
Error Response OutboundJaxrsResponse{status=500, reason=Internal Server Error, hasEntity=true, closed=false, buffered=false}
Any suggestion on this please?

Spring Soap Webservice exception handling

I am creating soap web service using spring. I am getting hibernate exception while trying to save the request.
I am trying to catch the hibernate exception in the catch block but control not even coming to the catch block
and soap service returning with the soap fault error message. Below are the list of classes which i am using,
Could any one please let me know how to handle the exception and rethrow the exception.
#WebService(serviceName = "submitService")
public class SubmitService extends AbstractWebServiceImpl {
#Autowired
private Validate validate;
#WebMethod
#SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
public ResponseData submitRequest(RequestData request) {
ResponseData response = validate.submitRequest(request);
return response;
}
}
My Base class
public class AbstractWebServiceImpl extends SpringBeanAutowiringSupport {
#Resource
private WebServiceContext webServiceContext;
protected void handleWSException(Exception e) throws BusinessFault, InfrastructureFault {
if ( e instanceof BusinessException) {
ReturnMessage rm = ((BusinessException)e).getReturnMessage();
throw new BusinessFault(rm, e);
} else if (e instanceof BaseException) {
ReturnMessage rm = ((BaseException)e).getReturnMessage();
throw new InfrastructureFault(rm, e);
} else {
ReturnMessage rm = new ReturnMessage(ReturnCode.GENERIC_WEB_SERVICE_ERROR, e.toString());
throw new InfrastructureFault(rm, e);
}
}
public void setWebServiceContext(WebServiceContext webServiceContext) {
this.webServiceContext = webServiceContext;
}
public WebServiceContext getWebServiceContext() {
return webServiceContext;
}
}
My Business layer implementation class
#Component
public class ValidateImpl implements Validate {
#Autowired
private SomeHibernateDao dao;
#Override
#Transactional
public ResponseData submitRequest(RequestData request) {
ResponseData response = new ResponseData();
try {
dao.save(request);
} catch (Exception e) {
// Control never execute this block of code if dao layer throwing any exception.
// I want to catch the exception here modify the response and return to the client
e.printStackTrace();
response.setErrorDetails("More meaningful error message");
}
return response;
}
This code returning default soap error message back to client.
I want to catch the exception and modify the exception before returning to client. Please let me know what change i have to make so that i can handle the soap error message before i return the response back to client.
I am able to catch the exception in SubmitService itself but not sure why not able to catch the exception in ValidateImpl. however my issue is resolved now.

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