I want to localize the exception messages thrown from POJO classes using Spring. I have a Spring MVC application through which I can add books. If the added book's title is null the implementation class throws an Exception. I want to localize this.
I know I can use the localeResolvers in the JSP pages and I have done that already. Can I leverage this to pick up localized error messages in the POJO? If so how do I inject the locale resolver (Cookie or Session) or locale which was set on the Cookie/Session into the POJO class?
addBook method throwing exception
public void addBook(IBook book) throws Exception {
if (book.getTitle() == null || book.getTitle() == "") {
throw new Exception("Title is null");
}
I want the throw new Exception("Title is null"); to be something like
String msg = rBundle.getMessage(propKey)
throw new Exception(msg);
where rBundle is a bundle object which knows its locale and the properties file from which it should pick the value for propKey
my controller class method which receives the form submission
#RequestMapping(method = RequestMethod.POST)
public String processSubmit(
#RequestParam("siteLanguage") String siteLanguage,
#ModelAttribute("book") Book book, BindingResult result,
SessionStatus status, HttpServletRequest arg0) {
logger.debug("Adding a Book");
Locale loc = RequestContextUtils.getLocale(arg0);
if (result.hasErrors()) {
return "error.htm";
} else {
try {
Author author = new Author("Gabriel Garcia Marquez");
book.setAuthor(author);
library.addBook(book);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "redirect:home.htm";
}
}
Is this possible? Or is it better I control the loading of java ResourceBundle for the respective locale.
Thanks
Why do you want to localize Exception?: I guess the exceptions are stored in a log file, the administrator should be able to read them, without knowledge of all the languages the users speak.
If you are talking about form validation, then have a look at spring form validation and jsr303 bean validation, both include concepts of localisation. (But both does not work with exceptions.)
Related
I'm trying to learn spring and to achieve that i'm building a REST application from scratch. I'm confused where should I check constraints in my application: Controller layer vs. Service layer.
For example, in create user method I want to check if there is any other user with the same email, since email is unique in my database. I also want to check if password matches(password and "confirm password" fields) etc.
Currently, in my implementation, all this things are verified in Controller layer so I can return a ResponseEntity for every approach.
#PostMapping("/signUp")
public ResponseEntity<Object> createUser(#RequestBody RegisterUserDto user) {
if (userService.getUserByEmail(user.getEmailAddress()) != null) {
return ResponseEntity.badRequest().body("email already exists");
}
if (!user.getPassword().equals(user.getConfirmPassword())) {
return ResponseEntity.badRequest().body("passwords are not the same");
}
User savedUser = null;
try {
savedUser = userService.createUser(userDtoConversions.convertToEntityRegister(user));
} catch (ParseException e) {
e.printStackTrace();
}
URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}")
.buildAndExpand(savedUser.getId()).toUri();
return ResponseEntity.created(location).build();
}
Create user method in Service layer:
#Override
#Transactional
public User createUser(User newUser) {
newUser.setDateCreated(new Date());
return userRepository.save(newUser);
}
So which approach is better? If I checks constraints and validations in Service layer, what should I return so I would know in my controller why create user fails?
In my mind the best place to handle exceptions is the service layer. For my a REST controller method should, at most, handle the request and pass it over to a service method.
With this approach you have very clearly defined layers that do a very clearly defined job. For example your service layer will handle the validation of the request, the persisting action and also will provide (if needed) a return object to the controller, which then will wrap into the appropriate response object (ResponseEntity in you case).
With that in mind, there is nothing stopping you to throw any kind of exceptions in the service layer and have translated into proper responses. Spring has a very neat and powerful mechanism that does precisely that which is called an exception handler.
So in your case for the password checking action you could do something like:
if (!user.getPassword().equals(user.getConfirmPassword())) {
throw new PasswordMismatchException("Passwords are not the same for user:: " + user.getName());
}
Where the PasswordMismatchException is a RuntimeException. With something like that, you can then go ahead and setup an ExceptionHandler along with the appropriate method to intercept this and translate it into a response. A simple example would be:
#RestControllerAdvice
public class ApplicationExceptionHandler {
#ExceptionHandler(PasswordMismatchException.class)
public ResponseEntity<String> handleBadPasswords(PasswordMismatchException e) {
return ResponseEntity.badRequest().body(e.getMessage());
}
}
You can read up for more on this on Spring's documentation:
Spring ExceptionHandler
Exception Handling in Spring
I have an #ExceptionHandler annotated method in a #ControllerAdvice annotated class.
This method must catch all exceptions, but after some default handlers have handled some exceptions, not as first handler (for example, it should not handle org.springframework.security.access.AccessDeniedException, otherwise the forwarding to the login page, which is done by Spring, does not work anymore).
I've found a lot of stuff but either old (not using #ControllerAdvice) or with Interfaces that cannot be used, because both html and JSON must fit to the return type of the method signature (using the class ResponseEntity, not some ModelAndView or String).
My current exception handler test method, which works fine:
#ExceptionHandler(Exception.class)
public ResponseEntity<?> handleExceptions(Exception ex, HttpServletRequest request) throws Exception {
HttpHeaders headers = new HttpHeaders();
//sort MIME types from accept header field
String accept = request.getHeader("accept");
if(accept != null) {
String[] mimes = accept.split(",");
//sort most quality first
Arrays.sort(mimes, new MimeQualityComparator());
//if json, return as json
String firstMime = mimes[0].split(";")[0];
if (firstMime.equals("application/json")) {
ExceptionWrapper exceptionVO = new ExceptionWrapper(ex);
headers.setContentType(MediaType.APPLICATION_JSON);
return new ResponseEntity<ExceptionWrapper>(exceptionVO, headers, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
//if not json, return as html
headers.setContentType(MediaType.TEXT_HTML);
String error = "<h1>Internal Server Error 500</h1><br>";
error += "Please copy the following text and send it to your server admin:<br><br>";
error += "<pre>" + ExceptionUtils.getStackTrace(ex) + "</pre>";
return new ResponseEntity<String>(error, headers, HttpStatus.INTERNAL_SERVER_ERROR);
}
It basically just decides in which format to return the error.
How can i lower the priority of this exception handler while still maintaining the Spring default exception handlers? Do i need to add some fancy Java-Config?
I already tried to create a class which implements ResponseEntityExceptionHandler and have overridden the handleExceptionInternal method there, which did not work. Spring complained it could not find a HttpStatus object to pass into the overridden method (ive tried providing that as bean, too).
I also tried using #Order on my #ControllerAdvice which changed nothing.
Spring Version 4.0.9
I am building a simple web applications with 3 layers - DAO, Service, MVC. When in my Controller I want to delete menu group and it contains menus I am getting ConstraintViolationException.
Where should I handle this exception? In DAO, Service, or in Controller? Currently I am handling the exception in Controller.
My code below.
DAO method for deleting menu groups:
#Override
public void delete(E e){
if (e == null){
throw new DaoException("Entity can't be null.");
}
getCurrentSession().delete(e);
}
Service method for deleting menu groups:
#Override
#Transactional(readOnly = false)
public void delete(MenuGroupEntity menuGroupEntity) {
menuGroupDao.delete(menuGroupEntity);
}
Controller method for deleting menu groups in Controller:
#RequestMapping(value = "/{menuGroupId}/delete", method = RequestMethod.GET)
public ModelAndView delete(#PathVariable Long menuGroupId, RedirectAttributes redirectAttributes){
MenuGroupEntity menuGroupEntity = menuGroupService.find(menuGroupId);
if (menuGroupEntity != null){
try {
menuGroupService.delete(menuGroupEntity);
redirectAttributes.addFlashAttribute("flashMessage", "admin.menu-group-deleted");
redirectAttributes.addFlashAttribute("flashMessageType", "success");
} catch (Exception e){
redirectAttributes.addFlashAttribute("flashMessage", "admin.menu-group-could-not-be-deleted");
redirectAttributes.addFlashAttribute("flashMessageType", "danger");
}
}
return new ModelAndView("redirect:/admin/menu-group");
}
You should handle exceptions in service layer only, as part of design unless required. Think of the requirement where you need a same functionality deleteMenu for some other mapping too.
From any design point of view. Keep controller very specific to handling model attributes only serving the request mapping to business logic. Keep a method in service layer to take menuGroupId and throw exception from that service if parameter is thrown or DB error has occurred.
Refer more: Model-View-Controller, what every part really does?
I am using spring 3.2 and I have come with one requirement and can't figure out how to achieve it, first please look for below
We mostly use model in Spring MVC which is use for data binding
#ResponseBody annotation returns the string as http response
So my requirement is I want to use both together in single method base on condition, Here is my code
#RequestMapping(value="userAddEditSubmit.htm", method={RequestMethod.GET, RequestMethod.POST})
public String userAddEditSubmit(
#ModelAttribute("user") User user,
HttpServletRequest request, HttpServletResponse response, HttpSession session,
Model model
) throws Exception {
try {
//Here is my logic
return "redirect:" + url;
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
So above is my method which returns specific jsp with model attribute, but now in one condition I have requirement to return String data instead of whole jsp in the same method, what can I do to achieve this? Any help will be highly appreciated.
You can simply return null from that method when your condition is met and write to the response yourself. Spring assumes that when a method returns null it has handled the response itself.
I have been looking for a way to make form validation as easy and unobtrusive as possible in Spring MVC 3. I like the way spring can handle Bean Validation by passing #Valid to my model (that has been annotated with validator annotations) and using the result.hasErrors() method.
I am setting up my controller actions like this:
#RequestMapping(value = "/domainofexpertise", method = RequestMethod.PUT)
public String addDomainOfExpertise(#ModelAttribute("domainOfExpertise")
#Valid DomainOfExpertise domainOfExpertise, final BindingResult result) {
if (result.hasErrors()) {
return "/domainofexpertise/add";
} else {
domainOfExpertiseService.save(domainOfExpertise);
return "redirect:/admin/domainofexpertise/list";
}
}
Which works like a charm. Database exceptions (like trying to save something with a unique constraint on a field) will still get through. Is there any way to incorporate catching those exceptions in the validation process going on behind the scenes? This way of validating is very concise so I want to avoid having to manually catch them in my controller.
Any information on this?
Here is an example I use to convert PersistentExceptions to a friendlier message. It is a method that goes in the Controller. Will this work for you?
/**
* Shows a friendly message instead of the exception stack trace.
* #param pe exception.
* #return the exception message.
*/
#ExceptionHandler(PersistenceException.class)
#ResponseBody
#ResponseStatus(HttpStatus.BAD_REQUEST)
public String handlePersistenceException(final PersistenceException pe) {
String returnMessage;
if (pe.getCause()
instanceof ConstraintViolationException) {
ConstraintViolationException cve =
(ConstraintViolationException) pe.getCause();
ConstraintViolation<?> cv =
cve.getConstraintViolations().iterator().next();
returnMessage = cv.getMessage();
} else {
returnMessage = pe.getLocalizedMessage();
}
if (pe instanceof EntityExistsException) {
returnMessage = messages.getMessage("user.alreadyexists");
}
return returnMessage;
}