I am having different projects for Service and Web. I would like to know how to handle when specific exception comes from Services. For example I am handling DuplicateDataException as follows at Service side:
public void serviceFunction()
{
try
{
//code
}catch(DuplicateDataException e)
{
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity(e.getMessage()).build();
}}
At UI side: controller class is calling the service function through Rest API
#RequestMapping(value = "/addNew", method = RequestMethod.POST)
public ModelAndView addNew(Object obj) {
try {
restTemplate.exchange(url, HttpMethod.POST, httpEntity,
Object.class);
LOGGER.info("Object Created Successfully");
} catch (Exception e) {
return ModelAndView("PageName", "param","value");
}
}
At UI side I am getting Internal Server Error, Instead I would like to get the entity error message value which was set at service side.
As a kind of best practice try to catch your exceptions in your service code and throw an RuntimeException("An error occured") or a self defined Exception which extends Java's RuntimeException. Then you can define a global ExceptionHandler for all of your controllers and return your own error page like:
#ControllerAdvice
public class MyExceptionHandler {
#ResponseStatus(HttpStatus.NOT_FOUND)
#ExceptionHandler(Exeption.class)
public ModelAndView handleFileNotFoundException(Exception exception){
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("yourView");
modelAndView.addObject("exception", exception);
return modelAndView;
}
}
Related
I'm new to Spring Boot. I'm trying to handle a exception when a value is null. Basically, its a value that I'm suppose to get from another external service (using restTemplate.exchange() method) which is currently down, so a null value gets assigned to that variable. Here's the code for the same service:
ResponseEntity<AuthenticationResponse> respNode = null;
try {
respNode = restTemplate.exchange(url, HttpMethod.POST, httpEntity, new ParameterizedTypeReference<AuthenticationResponse>() {});
} catch (Exception e) {
if (respNode == null) {
log.info("Unable to reach: {} ", url);
throw new AuthServerException("Auth Server Not Found");
}
}
I created a custom Exception to handle the scenario if the value is null. The Exception is thrown and I'm able to read it in the logs, but I'm not able to handle it.
AuthServerException.java
package com.nokia.sp.module.myVerify.portalapp.exception;
public class AuthServerException extends NullPointerException {
private static final long serialVersionUID = 1L;
public AuthServerException(String message) {
super(message);
}
}
Controller:
#Controller
#RequiredArgsConstructor
public class LandingPageController {
#ExceptionHandler(AuthServerException.class)
public ModelAndView AuthServerExceptionHandler(AuthServerException e) {
log.info(e.getMessage());
ModelAndView mav = new ModelAndView();
mav.setViewName("auth-server-handler");
return mav;
}
}
Am I missing something or have I made any syntax Error? Please do help me with the same. Thank you!
Right now i'm using this example of exception handling:
//get an object of type curse by id
//in the service file, this findCurseById() method throws a
//CursaNotFoundException
#GetMapping("/{id}")
public ResponseEntity<curse> getCursaById (#PathVariable("id") Long id) {
curse c = curseService.findCurseById(id);
return new ResponseEntity<>(c, HttpStatus.OK);
}
//so if not found, this will return the message of the error
#ResponseStatus(HttpStatus.NOT_FOUND)
#ExceptionHandler(CursaNotFoundException.class)
public String noCursaFound(CursaNotFoundException ex) {
return ex.getMessage();
}
and that's my exception
public class CursaNotFoundException extends RuntimeException {
public CursaNotFoundException(String s) {
super(s);
}
}
in future I want to use Angular as front-end, so I don't really know how I should treat the exceptions in the back-end. For this example let's say, should I redirect the page to a template.html page in the noCursaFound() method, or should I return something else? A json or something? I couldn't find anything helpful. Thanks
I would suggest keeping the error handling at the REST API level and not redirecting to another HTML page on the server side. Angular client application consumes the API response and redirects to template.html if needed.
Also, it would be better if the backend returns an ApiError when an exception occurs with a message and, optionally, an error code:
public class ApiError {
private String message;
private String code;
}
and handle the exceptions in a separate class, ExceptionHandler annotated with #ControllerAdvice:
#ControllerAdvice
public class ExceptionHandler {
#ExceptionHandler(value = CursaNotFoundException.class)
public ResponseEntity cursaNotFoundException(CursaNotFoundException cursaNotFoundException) {
ApiError error = new ApiError();
error.setMessase(cursaNotFoundException.getMessage());
error.setCode(cursaNotFoundException.getCode());
return new ResponseEntity(error, HttpStatus.NOT_FOUND);
}
#ExceptionHandler(value = Exception.class)
public ResponseEntity<> genericException(Exception exception) {
ApiError error = new ApiError();
error.setMessase(exception.getMessage());
error.setCode("GENERIC_ERROR");
return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
I work with 3-tier architecture in a Spring Boot app. I created 3 packages (model, service, controller), but what I did, service calls a repo function with try catch, and then I call it in controller
Example:
Service:
public ResponseEntity<List<Customer>> getAllCustomers() {
try {
List<Customer> customers = new ArrayList<Customer>();
cutomerRepository.findAll().forEach(customers::add);
if (customers.isEmpty()) {
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
return new ResponseEntity<>(customers, HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Controller
#GetMapping("/viewList")
private ResponseEntity<?> getAllCustomers()
{
try{
return customerService.getAllCustomers();
}catch (Exception exception){
return new ResponseEntity<String>("Customers is not found", HttpStatus.METHOD_FAILURE);
}
}
Is that correct? I think I should put in services only customerRepository.findAll() without any other logic or code, but I'm not sure. Any idea?
The Service layer should contain logic, so that is OK.
But it should not contain any classes from the Controller layer, as this would leak information from an "upper" layer into a "lower" layer. This means your Service should not return a ResponseEntity as this is from the Controller layer. Instead it should return simply a list of Customers and let the Controller construct the ResponseEntity out of it.
Otherwise your Service will always be limited to be called by this specific Controller. It would not be reusable to be called by another service of a different type of Controller, that does not use an HTTP ResponseEntity.
The best approach in my opinion is the following.
Your Service layer should not return ResponseEntity<List<Customer>> as it currently does. It should instead return List<Customer>.
This is already in the above answer but wanted to answer to extend the content a bit more.
The service also when modified to return List<Customer> should handle the exceptions with Application specific exceptions. So you create your own exception for your application, the model for this exception and also you create an Exception Advice class where all those application exceptions are handled in a general way. So your service will just throw the exception, the controller will not catch it and it will be handled by the Advice class (annotated with #ControllerAdvice) which will handle all the uncaught exceptions and return appropriate responses. There are also some more options to handle exceptions in generic way in Spring.
I am attaching the following code as an example
Class to handle all exceptions that bubble up from controllers.
#ControllerAdvice
public class ErrorHandler {
#ExceptionHandler(ApplicationException.class)
public ResponseEntity handleApplicationException(ApplicationException e) {
return ResponseEntity.status(e.getCustomError().getCode()).body(e.getCustomError());
}
}
Some application specific exception (The name could be more specific)
#Getter
#Setter
public class ApplicationException extends RuntimeException {
private CustomError customError;
public ApplicationException(CustomError customError){
super();
this.customError = customError;
}
}
An Error object to be returned to the client when exception happens
#Getter
#Setter
#NoArgsConstructor
public class CustomError {
private int code;
private String message;
private String cause;
public CustomError(int code, String message, String cause) {
this.code = code;
this.message = message;
this.cause = cause;
}
#Override
public String toString() {
return "CustomError{" +
"code=" + code +
", message='" + message + '\'' +
", cause='" + cause + '\'' +
'}';
}
}
Your Service
public List<Customer> getAllCustomers() {
try {
List<Customer> customers = new ArrayList<Customer>();
cutomerRepository.findAll().forEach(customers::add);
if (customers.isEmpty()) {
throw new ApplicationException(new CustomError(204, "No Content", "Customers do not exist"));
}
return new ResponseEntity<>(customers, HttpStatus.OK);
} catch (Exception e) {
throw new ApplicationException(new CustomError(500, "Server Error", "Disclose to the client or not what the cause of the error in the server was"));
}
}
The controller it self could also inspect the input information that it receives and if needed could throw it self an application specific exception or just return an appropriate response with what is false in the input.
This way the Controller is just handling the input/output between the user and the service layer.
The Service is just handling input/output of data from persistent layer.
I want to make exception handler for my WebClient, which calls external API. I don't want to use onStatus() method, due to I have abstract web client with different methods where I have to process exceptions, so I have to copy paste each onStatus() in my every abstract method. I want to make something similar to the rest template approach: we can implement ResponseErrorHandler and add our implementation into resttemplate e.g. setExceptionHandler(ourImplementation). I want the one class to handle all the exceptions.
Thanks for your advice in advance!
You can do smth like that
#ControllerAdvice
public class ErrorControllerAdvice {
#ExceptionHandler({RuntimeException.class})
public ResponseEntity<?> handleRuntimeException(RuntimeException e) {
log.error(e.getMessage(), e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage());
}
#ExceptionHandler({Exception.class})
public ResponseEntity<?> handleRuntimeException(Exception e) {
log.error(e.getMessage(), e);
return ResponseEntity.status(HttpStatus.OK)
.body(e.getMessage());
}
}
#Bean
public WebClient buildWebClient() {
Function<ClientResponse, Mono<ClientResponse>> webclientResponseProcessor =
clientResponse -> {
HttpStatus responseStatus = clientResponse.statusCode();
if (responseStatus.is4xxClientError()) {
System.out.println("4xx error");
return Mono.error(new MyCustomClientException());
} else if (responseStatus.is5xxServerError()) {
System.out.println("5xx error");
return Mono.error(new MyCustomClientException());
}
return Mono.just(clientResponse);
};
return WebClient.builder()
.filter(ExchangeFilterFunction.ofResponseProcessor(webclientResponseProcessor)).build();
}
We can use ResponseProcessor to write logic and handle different http statuses. This solution was tested and it works.
GithubSample
help me anybody Please in this issue.
The project, I am working on is old mvc, and is not going to be change to rest, So have to deal with "what we have :) ".
this is my controller method, the class of which is anotated #Controller
#RequestMapping(method=RequestMethod.POST)
public String createSomething(#RequestBody somejson, Model m) throws Exception {
SomeCustomListenerClass listener = new SomeCustomListenerClass(m);
AnnotherClass ac = somejson.toNotification(someService, anotherService, listener);
try {
ac = someService.createSomething(ac, listener);
m.addAttribute("success", true);
m.addAttribute("notificationId", ac.getId());
}
catch(SawtoothException ex) {
return handleError(ex, "Create Notification", listener);
}
return "structured";
}
and this one is handleError method body
private String handleError(Exception ex, String operation, SomeCustomListenerClass listener) {
if (!listener.hasErrors()) {
log.error("Unexpected error getting notification detail", ex);
listener.error("notification.controllerException", operation);
}
return "error";
}
Now I am getting the right errors in the client side, say in browser, but also getting the status code 500
now my boss says that we have to get 400, when validation errors hapens, not 500, as is now.
So, Please help me guys, how to overcome to this problem.
You can extend your exceptions and throw them on your controller:
#ResponseStatus(value=HttpStatus.BAD_REQUEST, reason="Your exception message")
public class YourCustomException extends RuntimeException {
}
Or you can use an ExceptionControllerHandler:
#Controller
public class ExceptionHandlingController {
// #RequestHandler methods
...
// Exception handling methods
// Convert a predefined exception to an HTTP Status code
#ResponseStatus(value=HttpStatus.CONFLICT,
reason="Data integrity violation") // 409
#ExceptionHandler(DataIntegrityViolationException.class)
public void conflict() {
// Nothing to do
}
// Specify name of a specific view that will be used to display the error:
#ExceptionHandler({SQLException.class,DataAccessException.class})
public String databaseError() {
// Nothing to do. Returns the logical view name of an error page, passed
// to the view-resolver(s) in usual way.
// Note that the exception is NOT available to this view (it is not added
// to the model) but see "Extending ExceptionHandlerExceptionResolver"
// below.
return "databaseError";
}
// Total control - setup a model and return the view name yourself. Or
// consider subclassing ExceptionHandlerExceptionResolver (see below).
#ExceptionHandler(Exception.class)
public ModelAndView handleError(HttpServletRequest req, Exception ex) {
logger.error("Request: " + req.getRequestURL() + " raised " + ex);
ModelAndView mav = new ModelAndView();
mav.addObject("exception", ex);
mav.addObject("url", req.getRequestURL());
mav.setViewName("error");
return mav;
}
}
Try the #ExceptionHandler annotation or #ControllerAdvice to create custom exception handling mechanisms:
https://www.tutorialspoint.com/spring_boot/spring_boot_exception_handling.htm
add #ResponseStatus(HttpStatus.BAD_REQUEST) on top of handleError(...) method.
#ExceptionHandler({ Throwable.class })
#ResponseStatus(HttpStatus.BAD_REQUEST)
public String handleError(...) {
...
}