Spring Boot. Different errors hanldilng for #Ccontroller and #RestController in one app - spring

I have normal #Controller and #RestController in one App.
How it possible to handle erros in Json for REST, and redirect to error page for normal #Controller?

You can make use of the Spring Annotation #ExceptionHandler in your controller and throw Exceptions in your controller logic. For an short example I will use Java's RuntimeException. You could define your own exception and throw them
// your controller classes
#Controller
public class MyController {
#ExceptionHanlder(RuntimeException.class)
public String errorInController(){
// for your custom page
return "yourDefineErrorTemplatePage";
// if you want to redirect to the default spring page
// return "redirect:/error";
}
#RequestMapping("yourFirstEndpoint")
public String getPage(){
if(yourLogicHere){
throw new RuntimeException("Display error page");
}
return "myPage";
}
}
Your #RestController could look like the following:
#RestController
public class RestControllerClass{
#ExceptionHandler(RuntimeException.class)
public ResponseEntity<Error> errorOccured(){
// you can return just a String or define your own 'error object'
Error error = new Error("Some error occured");
return new ResponseEntity<Error>(error, Http.Status.NOT_FOUND);
}
#RequestMapping("yourSecondEndpoint")
public ResponseEntity<YourEntity> getPage(){
// the entity you want do return as json
YourEntity yourEntity = new YourEntity();
if(yourLogicHere){
throw new RuntimeException("Display error page");
}
return new ResponseEntity<YourEntity>(yourEntity, HttpStatus.OK);
}
}
Example for the Error Object:
public class Error{
private String errorMessage;
public Error(String errorMessage){
this.errorMessage = errorMessage;
}
}
I hope this small example can solve your Problem.
For more details visit: https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc

Related

Pass service's function in controller for not duplicating try catch block

I am developing REST API in Spring boot with Hibernate.
I have this function in my controller
#PostMapping("/profile")
public ResponseEntity<String> saveProfile(#Valid #RequestBody SaveProfileVM saveProfileVM,
BindingResult bindingResult)
throws JsonProcessingException {
if (bindingResult.hasErrors()) return super.fieldExceptionResponse(bindingResult);
Profile profile;
boolean optimisticLockException = true;
int retryCount = 0;
do {
try {
profile = accountService.saveProfile(saveProfileVM.getAccountId(),
saveProfileVM.getName(),
saveProfileVM.getEmail());
optimisticLockException = false;
retryCount++;
} catch (ObjectOptimisticLockingFailureException exception) {
retryCount++;
System.out.println(exception.getMessage());
}
} while (optimisticLockException && retryCount < MAX_OPTIMISTIC_LOCK_EXCEPTION_RETRY_COUNT);
return ResponseEntity.status(HttpStatus.OK).body(objectMapper.writeValueAsString(profile));
}
and MAX_OPTIMISTIC_LOCK_EXCEPTION_RETRY_COUNT is 3
I don't want to duplicate the do..while and try..catch blocks in every method where I need to check ObjectOptimisticLockingFailureException
do {
try{}
catch{}
} while()
Is there any way that I can pass accountService.saveProfile() to a general method that has the do..while and try..catch block so that I don't have to copy and paste the blocks to every method I need?
Every controller extends a BaseController so, it might be good to have the general method in BaseController?
#RestController
#RequestMapping("/account")
public class AccountController extends BaseController {
Can you guys give an idea please?
You can use spring-retry. More details
#Retryable(value = ObjectOptimisticLockingFailureException.class, maxAttempts = 3)
public void saveProfile(Long accountId, String name, String email){..}

Returning proper value from #AfterThrowing

I am new to String, SpringBoot.
Can we suppress thrown exception in a method annotated with #AfterThrowing?
I mean when an exception is thrown, it will suppress that and will return a default value on behalf of the invoking method?
Say, I have a controller -
#RestController
public class MyRestController implements IRestController{
#Override
#GetMapping("hello-throw")
public String mustThrowException(#RequestParam(value = "name")final String name) throws RuntimeException {
System.out.println("---> mustThrowException");
if("Bakasur".equals(name)) {
throw new RuntimeException("You are not welcome here!");
}
return name + " : Welcome to the club!!!";
}
}
I have created a #AspectJ, as follows -
#Aspect
#Component
public class MyAspect {
#Pointcut("execution(* com.crsardar.handson.java.springboot.controller.IRestController.*(..))")
public void executionPointcut(){
}
#AfterThrowing(pointcut="executionPointcut()",
throwing="th")
public String afterThrowing(JoinPoint joinPoint, Throwable th){
System.out.println("\n\n\tMyAspect : afterThrowing \n\n");
return "Exception handeled on behalf of you!";
}
}
If I run this & hit a ULR like - http://localhost:8080/hello-throw?name=Bakasur
I will get RuntimeException, but, I want to return a default message like - Exception handeled on behalf of you!, can we do it using #AfterThrowing?
I know it can be done using #Around, but around will be called on every hit of the url, that I do not want
What you want to do is Exception Handling on the controller. You don't need to build it yourself, Spring already supports you with some annotations like #ExceptionHandler and #ControllerAdvice. Best would be to follow this example: https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc#using-controlleradvice-classes
#ControllerAdvice
class GlobalControllerExceptionHandler {
#ResponseStatus(HttpStatus.CONFLICT) // 409
#ExceptionHandler(DataIntegrityViolationException.class)
public void handleConflict() {
// Nothing to do
}
}
#ControllerAdvice
class GlobalDefaultExceptionHandler {
public static final String DEFAULT_ERROR_VIEW = "error";
#ExceptionHandler(value = Exception.class)
public ModelAndView
defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
// If the exception is annotated with #ResponseStatus rethrow it and let
// the framework handle it - like the OrderNotFoundException example
// at the start of this post.
// AnnotationUtils is a Spring Framework utility class.
if (AnnotationUtils.findAnnotation
(e.getClass(), ResponseStatus.class) != null)
throw e;
// Otherwise setup and send the user to a default error-view.
ModelAndView mav = new ModelAndView();
mav.addObject("exception", e);
mav.addObject("url", req.getRequestURL());
mav.setViewName(DEFAULT_ERROR_VIEW);
return mav;
}
}
You should use the fully qualified name of the class before method's name when you're referring to a pointcut. So, you should change #AfterThrowing something like this.
#AfterThrowing(pointcut="packageName.MyAspect.executionPointcut()",
throwing="th")
Please note that packageName is full package name of MyAspect.

Spring Boot Exception(Error) Handling for RESTful Services

I have the following RESTful Services method :
#PostMapping("/ajouterNewField")
public String ajouterField(#Valid #ModelAttribute("field") Fields field, Model model) throws IOException {
fieldDao.save(field);
// SOME CODE
return displayListeChamps( model);
}
The method is working fine and my question is how to handle any error (database not connected ...) or every issue that can happen durring the execution of this RESTful Services method.
You can use #ControllerAdvice
Refer to the code below
#ControllerAdvice
public String NyExceptionHandlerAdvice {
private final Logger logger = ...;
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
#ExceptionHandler({MyRunTimeException.class})
public void handleMyRunTimeException(Exception e) {
logger.error("Exception : ", e);
}
return MY_ERROR_STRING;
}
Best Practice is:
You can have your code throw RunTimeExceptions and handle all of them together or separately in handler methods similar to handleMyRunTimeException above.
You can decide what status code your request should return upon exception.
Basically you'll have to a sort of exception handler for any kind of exception your method might throw:
public class FooController{
// ...
#ExceptionHandler({ CustomException1.class, CustomException2.class })
public void handleException() {
//
}
}
Here's a nice article about that: https://www.baeldung.com/exception-handling-for-rest-with-spring

Dynamic message for custom exception annotated as ResponseStatus

I'm trying to provide dynamic message for my custom Exception like in code snippet below:
#ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "Entity not found")
public class EntityNotFoundException extends RuntimeException {
public EntityNotFoundException(String msg) {
super(msg);
}
}
But always when I throw it like shown below:
throw new EntityNotFoundException("User entity not found");
in the browser, I get the message "Entity not found" instead of "User entity not found".
How to achieve this?
I was stuck on this, But I just removed the reason side of the #ResponseStatus and It works, so your code should be like this:
#ResponseStatus(value = HttpStatus.NOT_FOUND)
public class EntityNotFoundException extends RuntimeException {
public EntityNotFoundException(String msg) {
super(msg);
}
}
And now you can set custom message by the constructor

Map UnsupportedMediaTypeException using ExceptionMapper

Is there a place where it is clearly documented that I cannot map UnsupportedMediaTypeException (because it's a rest easy exception and not custom application exception) using the javax.ws.rs.ext.ExceptionMapper?
I want to prove that to my client. Or another thing I would like to do is map this exception to a Response that can be fetched at the client to show the error. Right now when this exception is thrown it provides no information to the client as the application ends abruptly.
Any help would be appreciated.
Thanks
You can map this exception. Why not? Do you get an error?
This code should do the job
#Provider
public class EJBExceptionMapper implements ExceptionMapper<org.jboss.resteasy.spi.UnsupportedMediaTypeException>{
Response toResponse(org.jboss.resteasy.spi.UnsupportedMediaTypeException exception) {
return Response.status(415).build();
}
}
Don't forget to declare that provider in Spring configuration file.
If you want to provide more information to the client create class
#XmlRootElement
public class Error{
private String message;
//getter and setter for message field
}
and then you can
#Provider
public class EJBExceptionMapper implements ExceptionMapper<org.jboss.resteasy.spi.UnsupportedMediaTypeException>{
Response toResponse(org.jboss.resteasy.spi.UnsupportedMediaTypeException exception) {
Error error = new Error();
error.setMessage("Whatever message you want to send to user");
return Response.entity(error).status(415).build();
}
}
If you don't want to use Error entity simply pass a string to Response.entity() call.
If you want to catch whatever is thrown in you application create generic exception mapper:
#Provider
public class ThrowableMapper implements ExceptionMapper<Throwable> {
public Response toResponse(Throwable t) {
ErrorDTO errorDTO = new ErrorDTO(code);
return Response.status(500).build();
}
}

Resources