I have this controller:
#RequestMapping(value = "*.xls", method = RequestMethod.GET)
public String excel(Model model) {
return "excel";
The excel wiew opens actually a ExcelViewer, which is build in method
protected void buildExcelDocument(Map<String, Object> map, WritableWorkbook ww, HttpServletRequest hsr, HttpServletResponse hsr1) throws Exception {
Class.writecontent
Class.writeMoreContent
Called methods write content to the Excel sheet and they can throw e.g biffException. How can I show a certain error page when Exception is occured?
I tried this
#Controller
public class ExcelController
{
#ExceptionHandler(BiffException.class)
public String handleException(BiffException ex) {
return "fail";
}
#RequestMapping(value = "*.xls", method = RequestMethod.GET)
public String excel(Model model) {
return "excel";
}
}
But I'm getting the server's error message about Exceptions. Maybe a bean definition missing?
#ExceptionHandler-annotated methods only handle exceptions thrown by handler methods in the same class. Your exception, on the other hand, is being thrown from within the View's render method, at which point it's left the controller/handler layer.
Handling exceptions from within the view layer isn't well handled in Spring, mainly because it's hard to get it to work reliably with the servlet API, so I recommend you create a subclass of ExcelView and handle the exception in there.
Related
Here,my requirement is that i want separate code in my application for exception handling,i saw a nice option of spring there using #controller advice to handle exceptions globally.
#ControllerAdvice
class GlobalControllerExceptionHandler {
#ResponseStatus(HttpStatus.CONFLICT) // 409
#ExceptionHandler(DataIntegrityViolationException.class)
public void handleConflict() {
// Nothing to do
}
}
But there i want to cutomization there,like proper dynamic messages,own error code. so how can i do this,i am new to spring boot and even i don't have knowledge of spring.Need basic example.
You can come up with a class like this to capture information to be sent in response in case of exception:-
public class APIResponse {
int errorCode;
String description;
String someInformation;
// any other information that you want to send back in case of exception.
}
#ControllerAdvice
class GlobalControllerExceptionHandler {
#ResponseStatus(HttpStatus.CONFLICT) // 409
#ResponseBody
#ExceptionHandler(DataIntegrityViolationException.class)
public APIResponse handleConflict(DataIntegrityViolationException exception) {
APIResponse response = createResponseFromException(exception);
return response;
}
}
In your controller advice class:-
Have the return type APIResponse instead of void.
The handler method can have the exception raised as the argument.
Using the exception object to create the APIResponse object.
Put #ResponseBody on the handler method.
I am writing one REST api. There might be two exceptions in my DAO layer namely Exception_X and Exception_Y. If I encountered a exception Exception_X in DAO layer, my controller should return status code 200, if Exception_Y then 401 and if all goes well controller should return 201.
Now what was I thinking that I will throw encountered exception as it is from DAO layer to controller via service layer and in catch block of controller I will return response.
Is it acceptable or there is some other standard way?
Yes that is quite an acceptable way. However, rather than using try-catch, I would suggest to implement Exception Handlers for your REST Controllers. That way, you won't have to clutter your REST methods.
Also, it would be better to create a model object in REST layer for Error messages - ErrorResponse, with appropriate information:
class ErrorResponse {
int statusCode;
String errorMessage;
}
And return it's object from the exception handlers. BTW, you can also map your exception class directly to a response using #ResponseStatus annotation:
#ResponseStatus(value=401, reason="message")
class Exception_Y extends RuntimeException {
}
Then you don't have to write exception handler for that exception.
My suggestion would be wrap any unchecked exceptions with a service layer for loose coupling, and clean abstraction. Keep your controller free from conditions and let Service layer take care of this pain.
Keeping security concern in mind if you exposing it externally wrap your exception with service oriented exception it also helps to achieve generic layer specific exceptions say PersistentException, ServiceException etc. keeping good degree of decoupling in mind.
For handling exception globally you can use spring inbuild ControllerAdvice annotation with JsonExceptionModel for formatted error response.
#ControllerAdvice
public class GlobalExceptionHandler {
#ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
#ExceptionHandler(SQLException.class)
public Map<String, Object> handleSQLException(HttpServletRequest request, Exception ex) {
//json response here
}
}
public class JsonExceptionModel {
private int code;
private String type;
private String url;
private String message;
private String moreInfo;
// getters/setters here
}
I suggest you to go with Exception Resolver which is providing by spring.
Spring Framework provides HandlerExceptionResolver interface that we can implement to create global exception handler. We can also override it to create our own global handler with our application specific changes, such as logging of exception messages.
Here is the sample implementation of HandlerExceptionResolver,which will fullfill your need
public class RestResponseStatusExceptionResolver extends HandlerExceptionResolver {
#Override
protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) {
if (ex instanceof InvalidInputException) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return handleException(ex);
} else if (ex instanceof ResourceNotFoundException) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return handleException(ex);
}
//Adding error details to modelView object
modelAndView.addObject("errors", ErrorDetails);
// Custom error message details
public class ErrorDetails {
private String code;
private List<String> data;
}
What is the significance of return type in spring controllers which are used for download. Please consider the following use case:
public ModelAndView execute(final HttpServletRequest request, final HttpServletResponse response) {
try {
//some code.
} catch {
//handle the exception and build a error model and view. This model and view
//gives a lot of freedom for error handling in case of download fails on the
//same page without change in URL(enabling refresh of the same page again
//and again)
return modelAndView;
}
return null;
}
but generally I have seen controllers which has void return types which would look like the one below
public void execute(final HttpServletRequest request, final HttpServletResponse response) {
try {
//some code.
} catch {
//handle the exception but you cannot display the error with out leaving the same page. Error embedding is not possible without changing the URL.
}
}
I have two question here:
a) Are their any disadvantages of one approach over other. I see first serves more use cases than second.
b)Is there any disadvantage of returning null instead of ModelAndView.
References:
Downloading a file from spring controllers
Error handling by redirection in spring download file controller
Nothing bad as for marking method as void. You are handling download action via HttpServletResponse.
There are suggestions that FileSystemResource is cleaner but take into account that for e.g. there are cases that you need to forward your data to some other place in order to compose the report in the other place.
Also Spring lets you easily handle exceptions even when your return type in the controller is void:
#RequestMapping(value = "/pdf-report/{id}.pdf", method = RequestMethod.GET)
public void downloadPdfReport(#PathVariable String id, HttpServletRequest req, HttpServletResponse resp) throws Exception {
//supposed logic here
//if we are failing here then
throw new UserFriendlyException("Cannot produce data");
}
Then ControllerAdvice plays its role:
#ControllerAdvice
public class ExceptionControllerAdvice {
#ExceptionHandler(UserFriendlyException.class)
public ModelAndView handleUserFriendlyException(UserFriendlyException ex) {
//handle here your custom error page
}
}
More info on that from the Spring resources
I am having a Spring controller with a Validator defined as:
#InitBinder
protected void initBinder(WebDataBinder binder) {
binder.setValidator(new MyValidator(myService));
}
And calling it:
public ResponseEntity<?> executeSomething(
#ApiParam(name = "monitorRequest", required = true, value = "") #Valid #RequestBody MonitorRequest monitorRequest,
HttpServletRequest request, HttpServletResponse response) throws RESTException
I need to add one more Validator for this controller that could be called from some specific methods of this controller. Is there any way to achieve this?
EDIT: I am handling the Error by:
#ExceptionHandler(MethodArgumentNotValidException.class)
#ResponseBody
public ResponseEntity<?> processValidationError(MethodArgumentNotValidException ex) {
BindingResult result = ex.getBindingResult();
List<FieldError> fieldErrors = result.getFieldErrors();
ValidationErrorObj obj = processFieldErrors(fieldErrors);
ResponseEntity r = new ResponseEntity(obj, HttpStatus.BAD_REQUEST);
return r;
}
You can have more than one InitBinder method in a controller. It is controlled by the optional value parameter . For the javadoc of InitBinder : String[] value : The names of command/form attributes and/or request parameters that this init-binder method is supposed to apply to ... Specifying model attribute names or request parameter names here restricts the init-binder method to those specific attributes/parameters, with different init-binder methods typically applying to different groups of attributes or parameters.
Another way would be to explicely call a complementary Validator in specific methods.
BTW : I can't see any Errors or BindingResult in your controller method signature : where do you find whether errors occured ?
For those who are still trying to figure out how to solve this in 2017. I was facing similar issues while trying to implement 2 validators in my RestController. I followed the approach mentioned above by #Serge Ballasta.
I ended up making 2 Model each of linked to their specific Validators. The Controller methods look something like
#RequestMapping(value = "register", method = RequestMethod.POST)
public ResponseEntity<User> register(#Valid #RequestBody UserRegisterRequest userRegisterRequest) {
return null;
}
#RequestMapping(value = "test", method = RequestMethod.POST)
public ResponseEntity<?> test(#Valid #RequestBody TestRequest testRequest) {
return null;
}
and I created 2 initBinders to wire these validators in the controller like
#InitBinder("testRequest")
public void setupBinder(WebDataBinder binder) {
binder.addValidators(testValidator);
}
#InitBinder("userRegisterRequest")
public void setupBinder1(WebDataBinder binder) {
binder.addValidators(userRegistrationRequestValidator);
}
Please note that the #RequestBody attributes (userRegisterRequest , testRequest) had to be provided as values in the #InitBinder() annotations.
By the way the in my code I handle the bindingResult in a custom ExceptionHandler class which extends ResponseEntityExceptionHandler which gives me freedom to do custom handling of the response.
I am trying to convert controllers from the old inheritance framework to the new annotations.
Here's an existing controller:
public class SelectedTabController extends AbstractController {
private TabSelectionHelper tabSelectionHelper;
public ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
String param = request.getParameter("selectedTab");
if (param != null)
tabSelectionHelper.setSelectedTabTo(param);
return null;
}
public void setTabSelectionHelper(TabSelectionHelper tabSelectionHelper) {
this.tabSelectionHelper = tabSelectionHelper;
}
And after conversion I have this:
#Controller
public class SelectedTabController {
private TabSelectionHelper tabSelectionHelper;
#Autowired
public SelectedTabController(#Qualifier(value = "tabSelectionHelper") TabSelectionHelper tabSelectionHelper) {
this.tabSelectionHelper = tabSelectionHelper;
}
#RequestMapping("/selectedTab")
public void selectTab(String selectedTab, HttpServletResponse response) throws Exception {
//String param = request.getParameter("selectedTab");
if (selectedTab != null)
tabSelectionHelper.setSelectedTabTo(selectedTab);
}
}
This works but there is a (redundant) HttpServletResponse object in the selectTab paramter list. If I remove it, then the JQuery call says the server returns 500 and the call fails.
Any help?
The stacktrace shows:
javax.servlet.ServletException: Could not resolve view with name 'selectedTab' in servlet with name 'prodman'
So it is trying to find a view and failing. However, there is NO view to display as its a backend callby JQuery.
I guess by declaring the response object, Spring thinks I will write the response.
How can I prevent Spring from trying to resolve a view?
When you use void as your return type Spring will by default try to determine the view name from your method name, unless it thinks you're directly writing the response (which it does when you have a HttpServletResponse as a parameter). Have a look at section 15.3.2.3 of the Spring 3 docs.
You might want to try changing the return type to ModelAndView and return null and see what happens (I'm not certain you can get away with a null view with #RequestMapping as it's not something that I have ever tried)