Why to use #ResponseBody with #ControllerAdvice in the case of RESTServices - spring-boot

I am learning about global exception handling in spring boot. I have a designed a controller annotated with #RestController which has a controller method that throws an exception. I have designed another class named GlobalExceptionHandling annotated with #RestControllerAdvice/#ControllerAdvice. It works fine and handles the exception when annotated with #RestControllerAdvice but doesn't work as expected when annotated with #ControllerAdvice. I am sharing my code and the responses i got on postman.
DemoController:
#RestController
public class DemoController {
#RequestMapping("exception/arithmetic")
public String controllerForArithmeticException()
{
throw new ArithmeticException("Divide by zero error");
}
#RequestMapping("exception")
public String controllerForException() throws Exception
{
throw new Exception("An exception occurred");
}
}
GlobalExceptionHandler: (with #RestControllerAdvice)
#RestControllerAdvice
public class GlobalExceptionHandler{
#ExceptionHandler(value = Exception.class)
public String handleException(Exception e)
{
return "Exception: " + e.getMessage();
}
#ExceptionHandler(value = ArithmeticException.class)
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
public String handleArithmeticException(ArithmeticException e)
{
return "ArithmeticException: " + e.getMessage();
}
}
Response on postman:
Status: 404 Bad Request
Response Body: ArithmeticException: Divide by zero error
Console: Nothing gets printed on console.
GlobalExceptionHandler: (with #ControllerAdvice)
#ControllerAdvice
public class GlobalExceptionHandler{
#ExceptionHandler(value = Exception.class)
public String handleException(Exception e)
{
return "Exception: " + e.getMessage();
}
#ExceptionHandler(value = ArithmeticException.class)
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
public String handleArithmeticException(ArithmeticException e)
{
return "ArithmeticException: " + e.getMessage();
}
}
Response on postman:
Status: 404 Bad Request
Response Body: {
"timestamp": "2020-02-15T12:41:40.988+0000",
"status": 404,
"error": "Not Found",
"message": "Divide by zero error",
"path": "/exception/arithmetic"
}
Console: Nothing gets printed on console.
Can you explain what exactly #ResponseBody do?

Related

Spring boot #Valid is not returning proper message

Controller Method
#PostMapping("/hello")
public Hello hello(#Valid #RequestBody Hello hello) {
return hello;
}
POJO
import jakarta.validation.constraints.NotBlank;
class Hello{
#NotBlank(message = "msg must be present")
String msg;
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
Upon hitting the above URL with the following payload
{
"msg":""
}
I am getting the following response.
{
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"detail": "Invalid request content.",
"instance": "/hello"
}
It should ideally specify the message msg must be present.
What's wrong here?
The following things have been already tried
added server.error.include-message: always in application.properties file
#ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<Object> handleConstraintViolationException(ConstraintViolationException e) {
return new ResponseEntity<Object>("ConstraintViolationException",
HttpStatus.BAD_REQUEST);
}
Thanks in advance 👏
Edit
I had a #RestControllerAdvice and it starts working fine, once i remove it. #RestControllerAdvice is needed in my case for the customization of exceptions.
You have to write a controller advice and return the interpolated message from the exception caught in handler for invalid method argument.
#RestControllerAdvice
public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
#Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
String bodyOfResponse = ex.getBindingResult().getFieldErrors().get(0).getDefaultMessage();
return new ResponseEntity(bodyOfResponse, HttpStatus.BAD_REQUEST);
}
}
And after that you should get back message msg must be present along with 400. In case you have more constraints, you should iterate over field errors, and get(0) is only for demonstration purpose.

"error": "Method Not Allowed", "trace": "org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'GET' not supported

I am trying to check my #RestController with Postman but when I enter localhost:8081/rest/teachers/remove/2 I gain this error
My RestController :
#RestController
#RequestMapping("/rest/teachers")
public class TeacherRestController {
private static final String TEACHER_MODEL = "teacher";
#Autowired
TeacherService teacherService;
#DeleteMapping("/remove/{id}")
public ResponseEntity<HttpStatus> deleteTeacher(#PathVariable("id") long id) {
try {
teacherService.removeTeacher(teacherService.getTeacherById((int) id));
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
It is my foolish mistake.
I sent "GET" request on the Postman.
When I switched it to "Delete", it worked.

Spring ConstraintViolation not caught

I'm using Spring Boot, and I have created a custom PathVariable validator, which I use as follows:
#GetMapping(value = "/test/{id}")
public ResponseEntity<Void> test(#PathVariable("id")
#Valid
#MyGUID(message = "ID_INVALID")
String id) {
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
The validor seems to do its job well - it passes when input is valid, and throws exception when not. The problem is, that my custom exception handler doesn't catch the ConstraintViolationException that is thrown. This is the top line of the stack trace:
javax.validation.ConstraintViolationException: ID_INVALID
Then I have my exeception handler class:
#ControllerAdvice
public class RestExceptionController extends ResponseEntityExceptionHandler {
#ExceptionHandler(value = ConstraintViolationException.class)
public ResponseEntity<ErrorResponse> handleConstraintViolationException(ConstraintViolationException ex,
HttpHeaders headers,
HttpStatus status,
WebRequest request) {
List<String> errorDetailsCodes = new ArrayList<>();
for (ConstraintViolation<?> violation : ex.getConstraintViolations()) {
errorDetailsCodes.add(violation.getMessage());
}
ErrorResponse errorResponse = new ErrorResponse(
status.name(),
status.value(),
ex.hashCode(),
errorDetailsCodes
);
logException(ex);
return new ResponseEntity<>(errorResponse, headers, status);
}
#ExceptionHandler(value = Exception.class)
public ResponseEntity<ErrorResponse> handleGeneralException(Exception ex) {
ErrorResponse errorResponse = new ErrorResponse(
HttpStatus.INTERNAL_SERVER_ERROR.name(),
HttpStatus.INTERNAL_SERVER_ERROR.value(),
ex.hashCode()
);
logException(ex);
return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
It is strange to me that despite the fact I have both a specific and a general exception handler, they are both skipped, and the system throws its own error:
{
"timestamp": 1568554746642,
"status": 500,
"error": "Internal Server Error",
"message": "ID_INVALID",
"path": "/api/logged-in-user/test/123"
}
How can I catch this exception and return my custom response?
Thanks.

Spring boot custom exception not getting printed in logs

I am trying to implement a custom exception for my Spring boot REST project. The custom exception gets called but shows no impact in the way error message is displayed.
This is the POJO I'm using for my custom errors:
public class ApiError {
private HttpStatus status;
private String message;
private List<String> errors;
public ApiError(HttpStatus status, String message, List<String> errors) {
super();
this.status = status;
this.message = message;
this.errors = errors;
}
public ApiError(HttpStatus status, String message, String error) {
super();
this.status = status;
this.message = message;
errors = Arrays.asList(error);
}
}
This is the exception handler I wrote:
#ControllerAdvice
#EnableWebMvc
public class ApiExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(Exception.class)
public final ResponseEntity<Object> handleAllExceptions(Exception ex, WebRequest request) {
System.out.println("Custom exception!!");
//List<String> details = new ArrayList<>();
//details.add(ex.getLocalizedMessage());
//System.out.println("Localize message:: "+ex.getLocalizedMessage());
// ExceptionResponse exceptionResponse = new ExceptionResponse(new Date(), ex.getMessage(),
//request.getDescription(false));
ApiError error = new ApiError(HttpStatus.INTERNAL_SERVER_ERROR,"Server Error", request.getDescription(false));
return new ResponseEntity<Object>(error, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Additionally, I'm defined the following method within my controller:
#RequestMapping(value = "/model", params = "number", method = RequestMethod.GET, produces = "application/json")
List<Model> getModel(HttpServletRequest request,#RequestParam(value = "codeNumber") String number) throws Exception{
List<Model> model = null;
try {
model = niiService.getModel(number);
}catch(RuntimeException e){
new Exception(e);
}
return model;
}
However, in stead of my custom POJO, I'm seeing the following exception:
{
"timestamp": 1547013989124,
"status": 500,
"error": "Internal Server Error",
"message": "Request processing failed; nested exception is org.springframework.transaction.CannotCreateTransactionException: Could not open JDBC Connection for transaction; nested exception is com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure\n\nThe last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.",
"path": "/model"
}
I was expecting the following JSON structure in stead:
{
"status": 500,
"message": "Server Error"
..
}
Please let me know, what I am missing to get the error response in the way I wanted.
You are missing the throw in front of the custom exception.
Also make sure that you are catching the right exception inside your controller.
Change
catch (RuntimeException e) {
//custom exception
new Exception(e);
}
To
catch (RuntimeException e) {
//custom exception
throw new Exception(e);
}
I missed adding getter and setter to APIError class. Hence, the response was not coming in the way I expected.

Handle 404 exception in Spring

I'm trying to catch 404 error on my Spring boot + Hibernate Server when Im going to some page that doesn't exist (for example http://localhost:8080/tes (http://localhost:8080/test is working)). I've created #ControllerAdvice class but it can't catch 404 exception (other works just fine). What is problem?
#ControllerAdvice
public class GlobalExceptionHandlerController {
#ResponseStatus(value = HttpStatus.NOT_FOUND)
#ExceptionHandler(value = NullPointerException.class)
#ResponseBody
public String handleNullPointerException(Exception e) {
System.out.println("A null pointer exception ocurred " + e);
return "nullpointerExceptionPage";
}
#ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
#ExceptionHandler(value = Exception.class)
#ResponseBody
public String handleAllException(Exception e) {
System.out.println("A unknow Exception Ocurred: " + e);
return "unknowExceptionPage";
}
#ExceptionHandler(NoHandlerFoundException.class)
#ResponseStatus(HttpStatus.NOT_FOUND)
#ResponseBody
public String handleResourceNotFoundException() {
return "notFoundJSPPage";
}

Resources