I have some code like this
#Controller
class SomeController
#GetMapping("/someAsync")
#ResponseBody
public String someAsync() {
throw new SomeException();
}
#GetMapping("/somePage")
public String somePage() {
throw new SomeException();
}
in this case, i want to redirect to default error page for "/somePage"
and I need to response like { HttpStatus:500, message: "something happened" }
I made this ControllerAdvice
#ControllerAdvice
class SomeAdvice
#ExceptionHandler(Exception.class)
#ResponseBody
public Object handle(Exception e) {
// in this method, I want to know this Exception was called by a method that contains "#ResponseBody"
}
Could I know the method signature like this way?
Related
In my #RestController I'm successfully handling JSONParse exceptions coming from #RequestBody (for example, a String wrongly entered into an Integer field). This is the code:
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ExceptionHandler({ HttpMessageNotReadableException.class })
public ValidationError handleException(HttpMessageNotReadableException ex) {
if (ex.getCause() instanceof InvalidFormatException) {
...
} else {
throw ex;
}
}
Now I want to move this to a #ControllerAdvice to be used by many controllers. Here it is:
#Order(Ordered.HIGHEST_PRECEDENCE)
#ControllerAdvice
public class RestExceptionHandler extends ResponseEntityExceptionHandler {
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ExceptionHandler({ HttpMessageNotReadableException.class })
public ValidationError handleException(HttpMessageNotReadableException ex) {
if (ex.getCause() instanceof InvalidFormatException) {
...
} else {
throw ex;
}
}
But Spring complains with the following:
Ambiguous #ExceptionHandler method mapped for [class org.springframework.http.converter.HttpMessageNotReadableException]: {public Object foo.bar.RestExceptionHandler.handleException(org.springframework.http.converter.HttpMessageNotReadableException), public final org.springframework.http.ResponseEntity org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler.handleException(java.lang.Exception,org.springframework.web.context.request.WebRequest) throws java.lang.Exception}
I can't override ResponseEntityExceptionHandler.handleException because it's final. What other options are there?
Using Spring Boot 2.4.3.
I can't override ResponseEntityExceptionHandler.handleException because it's final
You're supposed to override the protected ResponseEntity<Object> handleHttpMessageNotReadable(...) method instead for custom error handling.
You should not extend from ResponseEntityExceptionHandler class.
Check this answer https://stackoverflow.com/a/71119336/4757784
I have my Spring error controller, and I need to get the actual exception class so I can print stack trace and other things like that.
This is my error controller
#Controller
public class ErrorController implements org.springframework.boot.web.servlet.error.ErrorController {
#RequestMapping("/error")
public String handleError() {
return "somethingwentwrong";
}
#Override
public String getErrorPath() {
return null;
}
}
I know its not much, but I need the exception object to be able to do some extra handling.
If you wish to execute different code based on the TYPE of the exception thrown, you should look at using #ControllerAdvice along with #ExceptionHandler. Any exceptions that you do not handle with an #ExceptionHandler will then bubble up to the default ErrorController (though you could handle Exception in a handler and then all exceptions will be handled via your custom handler). Something like:
#ControllerAdvice
#RestController
public class CustomResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(MyException.class)
protected ResponseEntity<String> handleMyException(MyException ex) {
// Your code here
}
#ExceptionHandler(Exception.class)
protected ResponseEntity<String> handleException(Exception ex){
// Your code here
}
}
https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-exceptionhandler
No worries guys, I solved the problem, I just had to do some digging into springs built in error controller to get the stacktrace.
I would like to manage Exception thrown by simple Controller or RestController in two ways:
1) html redirection
2) Json error
I tested the code below :
#ControllerAdvice(annotations = Controller.class)
public class ExceptionHandlerController
{
#ExceptionHandler(Exception.class)
public ModelAndView handleException(HttpServletRequest _req, Exception _ex)
{
K_LOGGER.info("test");
return new ModelAndView();
}
}
#ControllerAdvice(annotations = RestController.class)
public class ExceptionHandlerRestController
{
#ExceptionHandler(Exception.class)
public ResponseEntity<String> handleException(HttpServletRequest _req, Exception _ex)
{
return new ResponseEntity<>("test", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
#RestController
public class GreetingController
{
#RequestMapping("/greetingexception")
public Greeting greetingException(#RequestParam(value = "name", defaultValue = "World") String name)
throws Exception
{
throw new Exception();
}
}
It doesn't work properly, I always pass by ExceptionHandlerController but not by ExceptionHandlerRestController.
I think it's because #RestController inherit of #Controller.
Do you have a other solution?
Try to add #Order(Ordered.HIGHEST_PRECEDENCE) annotation to rest exception handler. It may helps you.
eg04lt3r answer is correct, just though that more details might be useful for someone.
In case when you have global #ControllerAdvice and want to handle some exception in a different way in one of your Controllers you need to set #Order(Ordered.HIGHEST_PRECEDENCE) on the #ControllerAdvice which should have higher priority.
For example:
#ControllerAdvice
public class GeneralExceptionHandler {
#ExceptionHandler(Exception.class)
protected ResponseEntity<Error> handleException(Exception ex) {
...
}
}
#ControllerAdvice(assignableTypes = MyController.class)
#Order(Ordered.HIGHEST_PRECEDENCE)
public class MyExceptionHandler {
#ExceptionHandler(Exception.class)
protected ResponseEntity<Error> handleException(Exception ex) {
...
}
}
#Order is needed because on startup one of the handlers will register with higher order automatically, anyway and your exception handling will become unpredictable. For example I recently saw a case when if you start an app using bootRun gradle task MyExceptionHandler was primary, but when started as jar GeneralExceptionHandler was primary.
I have a REST web service controller that looks like this:
#RequestMapping(value = URIConstants.URL_DOCUMENT_SEARCH, method = RequestMethod.POST, produces = { MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE })
protected DocumentSearchResponse getDocuments(#Valid #ModelAttribute DocumentSearchRequest objDMSRequest,BindingResult bindingResult, HttpServletRequest objServletRequest) throws AppException
{
if (bindingResult.hasErrors())
{
//I want to throw my custom exception here
///Or can anyone suggest a more clean and efficient way
}
-----More code and logic
}
I have a custom exception and handlers that will throw invalid HTTP invalid request exception. The custom exception has errorcode and error description fields.
My requirement is is there a way to parse the error from the bindingresults to a custome exception and trow that in the controler.
What you can do:
return new ResponseEntity<String>(errorDescription,HttpStatus.BAD_REQUEST);
Or, you can do it hardcore if you really want to use Exception(not recommended):
try {
throw new CustomException();
} catch(CustomException e) {
e.printStackTrace();
return new ResponseEntity<String>(e.getErrorDescription(),e.getStatusCode());
}
By the way: returning a Exception it's not good, that's why I don't show it.
#Albert-Pinto first of all the way you are trying to do this is completely wrong. If you want to consume a object it should come as #RequestBody and not as just a simple object. What you have done in your example is a MVC way which we do with the web applications, not the way we do for web service. so the above code will become as
#Controller
#RequestMapping("/user")
public class UserController {
#RequestMapping(method=RequestMethod.POST)
public ResponseEntity create(#RequestBody User user) {
try {
throw new CustomException();
} catch(CustomException e) {
e.printStackTrace();
return new ResponseEntity<String>(e.getErrorDescription(),e.getStatusCode());
}
}
Its is as simple as that
1. Create a class which extends Exception.
class MyCustomException extends Exception{
MyCustomException(){
}
MyCustomException(Object e){
super(e)
}
}
2. Make your code throw same type of exception
#Controller
#RequestMapping("/user")
public class UserController {
#RequestMapping(method=RequestMethod.POST)
public ResponseEntity create(#Valid User user, BindingResult bindingResult) {
try{
if (bindingResult.hasErrors()) {
throw new MyCustomException();
}
} catch(MyCustomException e){
//do what ever you want to do with it
}
...
}
3. Man you already done... :)
I am developing a project using Spring REST web services, where I need to show graceful error messages when an exception/error occurs. I followed this tutorial (http://www.javacodegeeks.com/2013/02/exception-handling-for-rest-with-spring-3-2.html) for exception handling using SpringREST. I get the proper output when there is no exception/error i.e. in form of an XML. The issue arises when an exception occurs. Here is part of the code base where an exception occurs if I do not pass the testId in
localhost:8080/test?testId=
The class outputs a response in form of a XML, so when an exception occurs, instead of showing the error message as figure 1 below, it shows error message as figure 2. If I do "View Page Source", I get the correct exception message (as figure 1). But I need the exception message directly. Could anyone, please suggest a solution?
#RequestMapping(value = "/test",
method = RequestMethod.GET,
produces = "application/xml")
public #ResponseBody String testResource(
#RequestParam(value="testId", required=true) String testId)
throws CustomRestException{
if (testId == null || testId.equals(""))
{
LOG.error( "testResource(): " + TestUtilsException.NULL_TEST_ID_ERROR_MSG );
//The error message is: The test Id is required and cannot be null or empty
throw new CustomRestException(TestUtilsException.NULL_TEST_ID_ERROR_MSG);
}
}
Figure 1
Figure 2
Other helper classes:
#ControllerAdvice
public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
public RestResponseEntityExceptionHandler() {
super();
}
#ExceptionHandler(value = { CustomRestException.class })
#ResponseBody
protected ResponseEntity<Object> handleNotFound(final RuntimeException ex, final WebRequest request) {
final String bodyOfResponse = ex.getMessage();
return handleExceptionInternal(ex, bodyOfResponse, new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR, request);
}
}
public class CustomRestException extends RuntimeException {
public CustomRestException() {
super();
}
public CustomRestException(final String message, final Throwable cause) {
super(message, cause);
}
public CustomRestException(final String message) {
super(message);
}
public CustomRestException(final Throwable cause) {
super(cause);
}
}
The #ControllerAdvice approach should work, although I don't think there's any need for the base class - you can just use #ExceptionHandler with Spring 4. But you are returning a response body that cannot be converted to Xml (it's a plain String), so you are getting an empty response and probably a 405 instead of a 500. If you want an Xml response you have to provide a body that can be converted (or else provide an HttpMessageConverter that can do it).
Consider doing this.
public class BaseController{
#ExceptionHandler(value = { CustomRestException.class })
protected #ResponseBody ResponseEntity<ErrorResponse > handleNotFound(final RuntimeException ex, final WebRequest request) {
System.out.println("is executed in handler");
final String bodyOfResponse = ex.getMessage();
return new ResponseEntity<ErrorResponse >(new ErrorResponse (bodyOfResponse), null, HttpStatus.NOT_FOUND);
}
}
In your controller do this.
#Controller
public class HomeController extends BaseController {//Your code here}
And create this class.
#XmlRootElement
public class ErrorResponse {
public String error;
}
Finally add to your class the following code
if (testId == null || testId.equals(""))
{
throw new CustomRestException("DD");
}
That will create an XML response as follows.
This XML file does not appear to have any style information associated
with it. The document tree is shown below.
<successResponse> <error>DD</error> </successResponse>
This will handle all the exception an is not needed to add #ControllerAdvice, that seems need to add their own MessageConverters that is why the answer is not converted to XML, I read that here.
I added ,produces = "application/xml" and remove it, and is still working as I think you want. Please let me know if this was useful.