I'm new in Spring and also in JSP. I'm working in the project and I needed to create a page where application will be redirected in case of specific exceptions.
I have service's method which throws one of exceptions. This method is called in one of our page controller with #RequestMapping annotation. So to redirect to specific error page, I created two methods with #ExceptionHanlder which handle this exceptions in this controller. How it looks:
#ExceptionHandler(IllegalStateException.class)
public ModelAndView handleIllegalStateException (IllegalStateException ex) {
ModelAndView modelAndView = new ModelAndView("redirect:/error");
modelAndView.addObject("exceptionMsg", ex.getMessage());
return modelAndView;
}
But there wasn't enough. I also need to create ErrorPageController:
#Controller
#RequestMapping("/error")
public class ErrorPageController {
#RequestMapping(method = RequestMethod.GET)
public ModelAndView displayErrorPage() {
return new ModelAndView("error");
}
}
And now works displaying error page. But my problem is, that I can't display error message in JSP...
I have:
<h3>Error page: "${exceptionMsg}"</h3>
But I don't see a message ;/ Instead of it, I see message in URL:
localhost/error?exceptionMsg=Cannot+change+participation+status+if+the+event+is+cancelled+or+it+has+ended.
And it's wrong because in URL I want to have only an "localhost/error" and nothing more. This message I want to display in JSP.
To fix both of your issues (show the message, and have the proper url) you should in original code change you exception handler method to e.g.
#ExceptionHandler(IllegalStateException.class)
public RedirectView handleIllegalStateException(IllegalStateException ex, HttpServletRequest request) {
RedirectView rw = new RedirectView("/error");
FlashMap outputFlashMap = RequestContextUtils.getOutputFlashMap(request);
if (outputFlashMap != null) {
outputFlashMap.put("exceptionMsg", ex.getMessage());
}
return rw;
}
Why? If you want your attributes to persist through redirect, you need to add them to flash scope. The code above uses the FlashMap, from the docs
A FlashMap is saved before the redirect (typically in the session) and
is made available after the redirect and removed immediately.
If it were to be a normal controller method, you could have simply added RedirectAttributes as an argument, but on #ExceptionHandler methods, the arguments of RedirectAttributes are not resolved, so you need to add the HttpServletRequest and use the RedirectView.
You have to change ModelAndView to:
#ExceptionHandler(IllegalStateException.class)
public ModelAndView handleIllegalStateException (IllegalStateException ex) {
ModelAndView modelAndView = new ModelAndView("error");
modelAndView.addObject("exceptionMsg", ex.getMessage());
return modelAndView;
}
And have this part in error.jsp:
<h3>Error page: "${exceptionMsg}"</h3>
Related
In my Spring Boot Application, I'm currently leveraging the resources/public/error/404.html custom error page to show (automagically) this 404 page errors on invalid URLS.
Is there an easy way to retain this auto functionality, and add a simple log message (with the invalid URL) for every such 404 ?
Ideally with as little code as possible I would want some like :
//Some code
LOGGER.warn("Invalid URL " + request.url);
//Some more code
You need to define a custom ErrorViewResolver:
#Component
public class MyErrorViewResolver implements ErrorViewResolver {
#Override
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
if (HttpStatus.NOT_FOUND == status) {
LoggerFactory.getLogger(this.getClass()).error("error 404 for url " + model.get("path"));
return new ModelAndView("error404", model);
}
else {
return new ModelAndView("error", model);
}
}
}
This MyErrorViewResolver will be automatically called in the BasicErrorController class.
For a 404 error, the view "error404" will be displayed.
For the other errors, the view "error" will be displayed.
Views must be in the "templates" folder (resources/templates/error404.html).
Add NotFoundException handler.
public class BaseController {
// Logger declaration
#ExceptionHandler(NotFoundException.class)
#ResponseStatus(value = HttpStatus.NOT_FOUND)
#ResponseBody
public ErrorResponse handleNotFoundError(HttpServletRequest req, NotFoundException exception) {
List<Error> errors = Lists.newArrayList();
errors.add(new Error(String.valueOf(exception.getCode()), exception.getMessage()));
log.error(exception);
return new ErrorResponse(errors);
}
}
I'm new to Spring. I'm trying to learn it by doing instead of reading. So I found some stuff which is confusing. But it works. I want to know why and how?
#Controller
#RequestMapping("/ok")
public class MyController {
#RequestMapping(value = "/ok", method = RequestMethod.GET)
public ModelAndView findAllAccounts() throws Exception {
ModelAndView mav = new ModelAndView();
mav.setViewName("account");
mav.addObject("someText", "Listing all accounts!");
return mav;
}
#RequestMapping(value="/ok/{accountId}", method = RequestMethod.GET)
public ModelAndView findAccount(#PathVariable int accountId, Model model) {
ModelAndView mav = new ModelAndView();
mav.setViewName("account");
mav.addObject("someText", String.format("Showing account %d", accountId));
return mav;
}
}
For above code I found that.
working Get request url : http://localhost:8080/ok/
working Get request url : http://localhost:8080/ok/ok/888
But I was expecting url : http://localhost:8080/ok/ok/ should also work. But it doesn't work. Why? If http://localhost:8080/ok/ok/888 works why http://localhost:8080/ok/ok/ doesn't work?
Also when I deploy it in tomcat. It only works if named ROOT.war. If I change to XYZ.war, it doesn't work. Why?
The http://localhost:8080/ok/ok/ does not work as it treats your last ok as your accountId and it fails conversion to an integer. The best documenation is the javadoc
Your question about tomcat - I believe it should be fine with another name, only then you would have to access it as http://localhost:8008/XYZ/
I would like to use #exceptionhandler to capture an HTTP Status 500 - Expected session attribute. I would like to return a message to the same page i am on showing the user the error.
Can someone point me to an example on how i can handle this exception and return a message to the view instead of redirecting to another page.
This is what i have so far however the item in the view is not getting set with the error message;
#ExceptionHandler(HttpSessionRequiredException.class)
public RedirectView handleHttpSessionRequiredException(Exception ex, HttpServletRequest request) throws Exception
{
logger.info("In the handleHttpSessionRequiredException Handler Method");
String referrer = request.getHeader("referer");
RedirectView redirectView = new RedirectView(referrer);
redirectView.addStaticAttribute("errorMessage","Execute A Query Then Retry");
return redirectView;
}
View
<label id="errorMessage" name="errorMessage">${errorMessage}</label>
You can get the referer and forward or redirect to it. E.g.
#ExceptionHandler(HttpSessionRequiredException.class)
public String (HttpServletRequest request) {
String referrer = request.getHeader("referer");
...
FlashMap flashMap = RequestContextUtils.getOutputFlashMap(request);
flashMap.put("errorMessage","Execute A Query Then Retry");
return "redirect:/my/url";
}
The redirect URL is relative to the application path. You can extract it from the referer.
You can return a ModelAndView from an #ExceptionHandler method by doing something like the following.
#ExceptionHandler(IOException.class)
public ModelAndView handleIOException(IOException ex) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("someObject", new SomeObject());
modelAndView.setViewName("someView");
return modelAndView;
}
The problem is figuring out the current page you were on before. To my knowledge, there isn't a way to get the current model and view from inside an ExceptionHandler method so you won't have a good way of knowing what view to use.
I think your best bet is to catch and handle the Exception in your controller.
I have login controller methods like so:
#RequestMapping(value = "/home", method = RequestMethod.GET)
public String home(Locale locale, Model model) {
// do stuff with locale and model
// return an html page with a login form
return "home";
}
#RequestMapping(value = "/account/login", method = RequestMethod.POST)
public String login(Model model, /* username + password params */){
try {
// try to login
// redirect to account profile page
return "redirect:/account/profile";
} catch (LoginException e) {
// log
// here I want to reload the page I was on but not with a url /account/login
// possibly using a forward
model.addAttribute("error", e.getMessage());
return "forward:/home";
}
}
The above code works on successful log-in attempt. However, it fails when the log-in attempt fails because Spring's forward uses the current request with the same HTTP method. So because I used a POST to send my username/password (which caused log-in to fail), the forward will also use POST to go to the handler method for /home, home(), which is expecting a GET.
Is there any way in Spring to redirect to another controller method with a different HTTP method while maintaining the current model (since I want to show the error message)?
This is on Spring 3.2.1.
Do a redirect instead:
return "redirect:/home";
If you need Model attributes to be available after the redirect, you can use flash attributes.
What is the proper way to forward a request in spring to a different controller?
#RequestMapping({"/someurl"})
public ModelAndView execute(Model model) {
if (someCondition) {
//forward to controller A
} else {
//forward to controller B
}
}
All of the controller have dependencies injected by Spring, so I can't just create them and call them myself, but I want the request attributes to be passed on to the other controllers.
Try returning a String instead, and the String being the forward url.
#RequestMapping({"/someurl"})
public String execute(Model model) {
if (someCondition) {
return "forward:/someUrlA";
} else {
return "forward:/someUrlB";
}
}
You can use view name like "redirect:controllerName" or "forward:controllerName". The latter will reroute request to another controller and former will tell browser to redirect request to another url.
docs: https://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/htmlsingle/spring-framework-reference.html#mvc-redirecting-redirect-prefix
You can use Spring RedirectView to dispatch request from one controller to other controller.
It will be by default Request type "GET"
RedirectView redirectView = new RedirectView("/controllerRequestMapping/methodmapping.do", true);