Rest - How to send Http Error Response Without Stack Trace - java

I have an rest web service. If any exception thrown, web service return http 500 error. But I don't want to send this error response with exception stack trace. I just want to send with error code and error message. I didn't achieve this. How can I do this?
I already tried #ControllerAdvice and #ExceptionHandler annotations but I couldn't. When I used #ResponseStatus annotation, always send static "reason" value. How can I set this value? Thank for your help.
public class SendMessageController{
private Logger log = LogManager.getLogger(getClass());
#Autowired
private QueueService queueService;
#RequestMapping(value="/message/check", method = RequestMethod.POST, headers={ "content-type=application/json"})
public #ResponseBody
ApiResponse sendMessage(#RequestBody String requestMessage) throws Exception {
try {
return new ApiResponse(queueService.processRequestForJSONString(requestMessage);
} catch (Exception e) {
throw new GenericException(HttpStatus.INTERNAL_SERVER_ERROR, e.getMessage());
//throw e;
}
}
#ResponseStatus(value=HttpStatus.INTERNAL_SERVER_ERROR, reason="Exception Message")
public class GenericException extends Exception {
public HttpStatus httpCode;
public String errorMessage;
public GenericException(HttpStatus httpCode, String errorMessage){
this.httpCode = httpCode;
this.errorMessage = errorMessage;
//I can't set "reason"
}
}
}

There are many possible solutions and I'm pretty sure an ErrorHandler is a much better way to go.
#GetMapping(value="/{empId}", produces=MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<EmployeeInfoItem> getEmployeeInfo(#PathVariable("empId") Integer empId) {
try {
...
} catch (Exception e) {
logger.error( e.getMessage() );
return ResponseEntity.status(HttpStatus.FAILED_DEPENDENCY).build();
}
}

Related

Rest template result is getting null in api call in spring boot

I am using below code to call remote API to remove user id(http://localhost:8080/remove).
try {
final RestTemplate restTemplate = new RestTemplate();
final UriComponentsBuilder builder =
UriComponentsBuilder.fromUriString(url);
builder.queryParam("acdc_id", acdcId);
ResponseEntity<ServiceResponse> result =
restTemplate.exchange(
builder.toUriString(),
HttpMethod.DELETE,
null,
ServiceResponse.class);
}catch(Exception e){
//exception handling
}
Remote API return 200 http code for success flow(working fine), but when some user id will not available then API sent below custom response:
{
"error code": "404",
"error": "USER ID Node not found : xyz"
}
I have already ServiceResponse.java class to get above response, but Rest Template returning below error in this case.
org.springframework.web.client.HttpClientErrorException$NotFound: 404 null
at org.springframework.web.client.HttpClientErrorException.create(HttpClientErrorException.java:85)
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:122)
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:102)
at org.springframework.web.client.ResponseErrorHandler.handleError(ResponseErrorHandler.java:63)
at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:778)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:736)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:670)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:579)
My ServiceResponse class is,
#JsonIgnoreProperties(ignoreUnknown = true)
public class ServiceResponse {
#JsonProperty(value = "error code")
private String errorCode;
#JsonProperty(value = "error")
private String error;
/**
* #return the error.
*/
public String getError() {
return error;
}
/**
* #param error The error to set.
*/
public void setError(final String error) {
this.error = error;
}
/**
* #return the errorCode.
*/
public String getErrorCode() {
return errorCode;
}
/**
* #param errorCode The errorCode to set.
*/
public void setErrorCode(final String errorCode) {
this.errorCode = errorCode;
}
}
Could you please help me here to fix my issue, or provide any suggestion, how I can get proper response from API instead null error
As I mentioned in the comment, you're getting HttpClientErrorException, which should be caught and dealt with. You're catching the whole Exception class, but there is no code in it. Or you can use #ControllerAdvice and #ExceptionHandler together to achieve this as well.
You can use Controller Advice #ControllerAdvice annotation to handle the exceptions . You can send Custom response from the method. You can define exceptionHandler for HttpClientErrorException and send custom response from this method.
Please check https://www.tutorialspoint.com/spring_boot/spring_boot_exception_handling.htm for further details.
One more option is you can use CustomResponseErrorHandler something like below
#Component
public class RestTemplateResponseErrorHandler
implements ResponseErrorHandler {
#Override
public boolean hasError(ClientHttpResponse httpResponse)
throws IOException {
return (
httpResponse.getStatusCode().series() == CLIENT_ERROR
|| httpResponse.getStatusCode().series() == SERVER_ERROR);
}
#Override
public void handleError(ClientHttpResponse httpResponse)
throws IOException {
if (httpResponse.getStatusCode()
.series() == HttpStatus.Series.SERVER_ERROR) {
// handle SERVER_ERROR
} else if (httpResponse.getStatusCode()
.series() == HttpStatus.Series.CLIENT_ERROR) {
// handle CLIENT_ERROR
if (httpResponse.getStatusCode() == HttpStatus.NOT_FOUND) {
throw new NotFoundException();
}
}
}
}
And then use it like
#Bean
RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(new RestTemplateResponseErrorHandler());
return restTemplate;
}
Please check https://www.baeldung.com/spring-rest-template-error-handling for ResponseErrorHandler

Exception handling in StreamingResponseBody

I'm trying to catch an exception thrown in my implementation of StreamingResponseBody, I can see the exception being thrown inside the class however the thrown exception isn't visible to the method body or my Controller Advice. So none of my handling seems to work, just interested to know which is the correct way to handle exceptions in this case.
#GetMapping(path = "/test", produces = "application/json")
public StreamingResponseBody test(#RequestParam(value = "var1") final String test)
throws IOException{
return new StreamingResponseBody() {
#Override
public void writeTo(final OutputStream outputStream) throws IOException{
try {
// Some operations..
} catch (final SomeCustomException e) {
throw new IOException(e);
}
}
};
}
I would expect my ControllerAdvice to return an ResponseEntity with a Http Status of 500.
The best way I discovered to handle errors/exceptions in the web environment is to create your custom exception with the disabled stack trace, and handle it with #ControllerAdvice.
import lombok.Getter;
import org.springframework.http.HttpStatus;
public class MyException extends RuntimeException {
#Getter private HttpStatus httpStatus;
public MyException(String message) {
this(message, HttpStatus.INTERNAL_SERVER_ERROR);
}
public MyException(String message, HttpStatus status) {
super(message, null, false, false);
this.httpStatus = status;
}
}
And then handle it in #ControllerAdvice like this:
#ExceptionHandler(MyException.class)
public ResponseEntity handleMyException(MyException exception) {
return ResponseEntity.status(exception.getHttpStatus()).body(
ErrorDTO.builder()
.message(exception.getMessage())
.description(exception.getHttpStatus().getReasonPhrase())
.build());
}
where ErrorDTO is just a simple DTO with two fields:
#Value
#Builder
public class ErrorDTO {
private final String message;
private final String description;
}

Return HTTP code 200 from Spring REST API

I want to use this code to receive http link with values:
#PostMapping(value = "/v1/notification")
public String handleNotifications(#RequestParam("notification") String itemid) {
// parse here the values
return "result successful result";
}
How I can return http code 200 - successful response?
And also for example if there is a code exception into code processing how can I return error 404?
If you are using spring:
#PostMapping(value = "/v1/notification")
public ResponseEntity handleNotifications(#RequestParam("notification") String itemid) {
// parse here the values
return ResponseEntity.ok().build();
//OR ResponseEntity.ok("body goes here");
}
If you use #RestController it should return 200 by default.
But anyway, you can set a particular response status by #ResponseStatus annotation (even if the methods returns void) or you can return a custom response by ResponseEntity.
EDIT: added error handling
For error handling, you can return a particular response entity:
return ResponseEntity.status(HttpStatus.FORBIDDEN)
.body("some body ");
or you can use #ExceptionHandler:
#ExceptionHandler(Exception.class)
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public void handleError(Exception ex) {
// TODO: log exception
}
You can do it by annotating your method with #ResponseStatus using HttpStatus.OK (However it should be 200 by default), like this:
Some controller
#PostMapping(value = "/v1/notification")
#ResponseStatus(HttpStatus.OK)
public String handleNotifications(#RequestParam("notification") String itemid) throws MyException {
if(someCondition) {
throw new MyException("some message");
}
// parse here the values
return "result successful result";
}
Now, in order to return a custom code when handling a specific exception you can create a whole separate controller for doing this (you can do it in the same controller, though) which extends from ResponseEntityExceptionHandler and is annotated with #RestControllerAdvice and it must have a method for handling that specific exception as shown below:
Exception handling controller
#RestControllerAdvice
public class ExceptionHandlerController extends ResponseEntityExceptionHandler {
#ExceptionHandler(MyException.class)
protected ResponseEntity<Object> handleMyException(MyException ex, WebRequest req) {
Object resBody = "some message";
return handleExceptionInternal(ex, resBody, new HttpHeaders(), HttpStatus.NOT_FOUND, req);
}
}
You can do something like this:
#PostMapping(value = "/v1/notification")
public ResponseEntity<String> handleNotifications(
#RequestParam("notification") String itemid) {
// parse here the values
return new ResponseEntity<>("result successful result",
HttpStatus.OK);
}

Spring Cloud Hystrix : FallbackMethod not invoked

I am playing around Spring Cloud Hystrix and I got this weird error that my Fallback method is not being invoked. My Controller is below .
#Controller
public class DashboardController {
#LoadBalanced
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder){
return builder.build();
}
#Autowired
private RestTemplate restTemplate;
#HystrixCommand(fallbackMethod = "getFareBackup")
#RequestMapping("/dashboard")
public String getFareDashboard(Model m) {
try {
ResponseEntity<List<BusFare>> responseEntity = restTemplate.exchange("http://busfare-service/api/v1/fare/",
HttpMethod.GET, null, new ParameterizedTypeReference<List<BusFare>>() {
});
m.addAttribute("fareList", responseEntity.getBody());
} catch (Exception e) {
e.printStackTrace();
}
return "dashboard";
}
public String getFareBackup(Model m){
System.out.println("Fallback operation called");
m.addAttribute("fareList", new ArrayList<BusFare>().add(new BusFare(1, BigDecimal.valueOf(0.7), "Regular")));
return "dashboard";
}
}
As you can see, I set the fallbackMethod properly, however, when I run the server and point my browser to the end point, I get an exception saying that my server is down, as I understand when my service is down it should invoke the fallbackMethod, but it in my case that is not the case, my fallbackMethod is basically not being invoked.
java.lang.IllegalStateException: No instances available for busfare-service
I am missing something in my code?
It seems my like, Hystrix handles this fallbackMethod thru errorHandling. What messed up my code that caused my fallback not being invoked is the errorHandling.
#HystrixCommand(fallbackMethod = "getFareBackup")
#RequestMapping("/dashboard")
public String getFareDashboard(Model m) {
ResponseEntity<List<BusFare>> responseEntity = restTemplate.exchange("http://busfare-service/api/v1/fare/",
HttpMethod.GET, null, new ParameterizedTypeReference<List<BusFare>>() {
});
m.addAttribute("fareList", responseEntity.getBody());
return "dashboard";
}
With the code above, the fallbackMethod is now working.

Spring controller throwing HttpStatus.UNAUTHORIZED fires 500 Http error instead of 401

Here's the scenario :
I created the following custom response exception, to fire the 401 Http Status :
#ResponseStatus(value = HttpStatus.UNAUTHORIZED)
public class HttpUnauthorizedException extends RuntimeException {
}
The controller that uses the exception :
#Controller
public UserController {
#RequestMapping(value = "api/user")
#ResponseBody
public String doLogin(
#RequestParam(value = "username", required = false) String username, #RequestParam(value = "password", required = false) String password) {
if(userLoggedIn(String username, String password)) {
return "OK";
}
else {
throw new HttpUnauthorizedException();
}
}
...
}
Now when I try to access the controller to see the 401 exception, the server fires the Http error code 500 instead. But interestingly enough, when I try with the HttpStatus.NOT_FOUND it actually works, the server fires 404. Is there something I'm missing on here?
Thanks in advance :-)
First throw new HttpUnauthorizedException();
then you can catch it at a normal controller that have #ControllerAdvice annotation
#ControllerAdvice // To Handle Exceptions
public class ExceptionController {
//// ...........
#ExceptionHandler({HttpUnauthorizedException.class})
#ResponseBody
#ResponseStatus(value = HttpStatus.UNAUTHORIZED)
Map<String, String> unauthorizedAccess(Exception e) {
Map<String, String> exception = new HashMap<String, String>();
log.error("unauthorized Access to the API: " + e.getMessage(), e);
exception.put("code", "401");
exception.put("reason", e.getMessage());
return exception;
}
}
I think code should be much simpler, maybe the answer was written with old Spring version.
In this example I've implemented method to handle exception - HttpClientErrorException.Unauthorized to cover authentication issue (401):
#ControllerAdvice
public class MyErrorsHandler extends ResponseEntityExceptionHandler
{
#ExceptionHandler(HttpClientErrorException.Unauthorized.class)
protected ResponseEntity<Object> handleAuthenticationError(RuntimeException ex, WebRequest request)
{
return handleExceptionInternal(ex,
"Cannot login, please check your inputs",
new HttpHeaders(), HttpStatus.UNAUTHORIZED, request);
}
}
Finally I get correct error to GUI

Categories