Not showing error message in form(jsp) - spring

I have a problem with showing error message in my form(jsp form).
I create a validator, and want to see errors (if exists) in my form, but the errors not showing, what's problem?
Part of form
<form:form method="POST" action="/regStoreSuccessful" commandName="storeForm">
<table>
<tr>
<td><form:label path="name">Store name</form:label></td>
<td><form:input path="name" /></td>
<td><form:errors path="name" cssclass="error"/></td>
</tr>
Validator
public class StoreValidator implements Validator {
#Override
public boolean supports(Class<?> clazz) {
return Store.class.equals(clazz);
}
#Override
public void validate(Object target, Errors errors) {
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name", "name.empty", "Name field is empty");
}
}
Controller
#Autowired
private StoreValidator storeValidator;
#InitBinder
protected void initBinder(WebDataBinder binder) {
binder.setValidator(storeValidator);
}
//get method
#RequestMapping(value = "/regStore", method = RequestMethod.GET)
public ModelAndView addStore() throws SQLException {
ModelAndView modelAndView = new ModelAndView("Store/regStore", "storeForm", new Store());
}
//post method
#RequestMapping(value = "/regStoreSuccessful", method = RequestMethod.POST)
public ModelAndView addStorePost(#Valid #ModelAttribute("storeForm") Store storeForm, BindingResult bindingResult, Principal principal) throws SQLException {
ModelAndView modelAndView = new ModelAndView("redirect:body");
if(bindingResult.hasErrors()) {
modelAndView.addObject("errors", bindingResult.getAllErrors());
return new ModelAndView("redirect:regStore");
}
storeService.addStore(storeForm);
return modelAndView;
}

The model attributes won't be available after redirect, you should use RedirectAttributes redirectAttrs and store errors as flash attributes, that way attributes will be available after the redirect and removed immediately after used, so change your method to
//post method
#RequestMapping(value = "/regStoreSuccessful", method = RequestMethod.POST)
public ModelAndView addStorePost(#Valid #ModelAttribute("storeForm") Store storeForm, BindingResult bindingResult, Principal principal, , RedirectAttributes redirectAttrs) throws SQLException {
ModelAndView modelAndView = new ModelAndView("redirect:body");
if(bindingResult.hasErrors()) {
redirectAttrs.addFlashAttribute("errors", bindingResult.getAllErrors());
return new ModelAndView("redirect:regStore");
}
storeService.addStore(storeForm);
return modelAndView;
}

Related

Handle form submission in SpringBoot

Here is my controller:
#Controller
#RequestMapping("/accounts/*")
public class AccountController {
#Autowired
private AccountService accountService;
#GetMapping
public ModelAndView home() {
final ModelAndView modelAndView = new ModelAndView();
final List<Account> accountsForCurrentUser = this.accountService.getAccountsForCurrentUser();
modelAndView.addObject("accounts", accountsForCurrentUser);
modelAndView.setViewName("pages/accounts/index");
return modelAndView;
}
#GetMapping("create")
public ModelAndView create() {
final ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("account", new Account());
modelAndView.setViewName("pages/accounts/create");
return modelAndView;
}
#PostMapping("create")
public ModelAndView createSubmit(#Valid #ModelAttribute(name = "account") Account account, BindingResult bindingResult, ModelAndView modelAndView) {
if (bindingResult.hasErrors()) {
return modelAndView;
}
return new ModelAndView("redirect:/accounts");
}
}
What I'd like to do is redirecting user to /accounts/ when the form is validated but taking him back to /accounts/create/ with errors shown if errors has been reported.
But, on error, I have:
Error resolving template "accounts/create", template might not exist or might not be accessible by any of the configured Template Resolvers
You also need set model and view name in post/create method.
By the way, handling methods with ModelAndView is valid but I think it would be better to use the String approach. It's much better to read and a standart way. So your controller will look like:
#Controller
#RequestMapping("/accounts")
public class AccountController {
#Autowired
private AccountService accountService;
#GetMapping("")
public String home(Model Model) {
List<Account> accountsForCurrentUser = this.accountService.getAccountsForCurrentUser();
model.addAttribute("accounts", accountsForCurrentUser);
return "pages/accounts/index";
}
#GetMapping("/new")
public String newAccount(Model model) {
model.addAttribute("account", new Account());
return "pages/accounts/create";
}
#PostMapping("/new")
public String createAccount(#Valid #ModelAttribute Account account, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "pages/accounts/create";
}
"redirect:/accounts";
}
}

Spring ModelAndView Attribute is null

I have one simple controller and one interceptor.
Within interceptor in postHandle-method I am checking user.
Problem: My user-model is sometimes null, between controller-handles.
postHandle invoked by home-handle==> User-Model is not null
postHandle invoked by check_user-handle ==> User-model is null
postHandle invoked by redirectToErrorPage-handle ==> User-model is
not null anymore and contains everything, what i've expected by
check_user-PostHandle Invocation.
Here is my controller
#Controller
#SessionAttributes("user")
public class HomeController {
#RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView home(Model model, HttpServletRequest request, HttpSession session) {
User user = new User();
return new ModelAndView("login", "user", user);
////now User-Model was saved in the session
}
//now i'am redirectring user in the "check_user"-handle
#RequestMapping(value = "/check_user", method = RequestMethod.POST)
public ModelAndView checkUser(#Valid #ModelAttribute("user") User user, BindingResult bindingResult, Model model,
HttpServletRequest request, RedirectAttributes redirectAttr) {
RedirectView redirectView = null;
ModelAndView mav=null;
try {
if(!bindingResult.hasErrors()){
redirectView = new RedirectView("home");
redirectView.setStatusCode(HttpStatus.FOUND);
redirectAttr.addFlashAttribute("httpStatus", HttpStatus.FOUND);
mav = new ModelAndView(redirectView);
return mav; //at next i entry post-handle from interceptor,
//which says to me, that user-model is null.
}
}
//My interceptor redirects me to this handle.
//After this handle within interceptor method "postHandle", i see, that
//user-object exists
#RequestMapping(value = "/error", method = RequestMethod.GET)
public String redirectToErrorPage(HttpServletRequest request){
return "error";
}
}
And my Interceptor:
public class UserInterceptor extends HandlerInterceptorAdapter {
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
User user = (User) modelAndView.getModel().get("user");
if(user == null || !user.isAdmin()){
response.sendRedirect(request.getContextPath()+"/failed");
}
}
}
While I retrive, which keys my model has, when postHandle was invoked by "check_user", I have only one key "totalTime". Whats going on with my model?
Try modifying your postHandle() as below:
...
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
User user = (User) request.getSession().getAttribute("user");
...

Trying to pass objects to controller(Spring MVC)

I am trying to test my controller. Spring populates my Profile object but it is empty. I can set the email before the call bu it still is null. How to jag pass a Profile in a proper way?
private MockHttpServletRequest request;
private MockHttpServletResponse response;
#Autowired
private RequestMappingHandlerAdapter handlerAdapter;
#Autowired
private RequestMappingHandlerMapping handlerMapping;
#Before
public void setUp() throws Exception {
this.request = new MockHttpServletRequest();
request.setContentType("application/json");
this.response = new MockHttpServletResponse();
}
#Test
public void testPost() {
request.setMethod("POST");
request.setRequestURI("/user/"); // replace test with any value
final ModelAndView mav;
Object handler;
try {
Profile p = ProfileUtil.getProfile();
p.setEmail("test#mail.com");
request.setAttribute("profile", p);
System.out.println("before calling the email is " + p.getEmail());
handler = handlerMapping.getHandler(request).getHandler();
mav = handlerAdapter.handle(request, response, handler);
Assert.assertEquals(200, response.getStatus());
// Assert other conditions.
} catch (Exception e) {
}
}
This is the controller
#RequestMapping(value = "/", method = RequestMethod.POST)
public View postUser(ModelMap data, #Valid Profile profile, BindingResult bindingResult) {
System.out.println("The email is " + profile.getEmail());
}
Try using following signature for the controller function postUser.
public View postUser(ModelMap data, #ModelAttribute("profile") #Valid Profile profile, BindingResult bindingResult)
Hope this helps you. Cheers.

processFormSubmission not being called

I am new to spring. I am creating a simple login page. But the processFormSubmission() is not being called. But the showForm() is working.
public class LoginController extends SimpleFormController
{
private LoginService loginService;
private String loginView;
public LoginService getLoginService() {
return loginService;
}
public void setLoginService(LoginService loginService) {
this.loginService = loginService;
}
public String getLoginView() {
return loginView;
}
public void setLoginView(String loginView) {
this.loginView = loginView;
}
public LoginController() {
setBindOnNewForm(true);
}
#Override
protected ModelAndView processFormSubmission(HttpServletRequest request,
HttpServletResponse response, Object command, BindException errors)
throws Exception
{
TraceUser tr = (TraceUser) command;
System.out.println(tr);
//loginService.
return super.processFormSubmission(request, response, command, errors);
}
#Override
protected ModelAndView showForm(HttpServletRequest request,
HttpServletResponse response, BindException errors)
throws Exception {
ModelAndView mav = new ModelAndView();
mav.addObject("traceUser", new TraceUser());
mav.setViewName(getLoginView());
return mav;
}
}
And please help me out with how should the ModelAndView object should be processed further.
First of all, the use of the Controller API has been left aside in favor of the new annotation-based controllers (see the #RequestMapping annotation) and classes like SimpleFormController have been deprecated since quite a while now.
However, to answer your question, I assume your form does not declare method="post" and by default, the SFC will consider only POST requests as form submissions (see the isFormSubmission() method in AbstractFormController). Is this the case ?

Rollback transaction in a Spring #Controller and show exception message in view

I have a Spring MVC controller and when an exception occurs I would like to show the exception message in the view and rollback the open transactions. The view contains a form like this:
<form:form method="POST" modelAttribute="registrationForm">
<form:errors path="*" cssClass="error-message"/>
...
</form:form>
I would like to show the exception message in the view, using the <form:errors ... /> feature. This is my current quite horrible solution:
#RequestMapping(value = "/registration", method = RequestMethod.POST)
public ModelAndView submitForm(#ModelAttribute("registrationForm") RegistrationForm registrationForm,
BindingResult result,
ModelAndView modelAndView,
HttpServletRequest request) throws Exception
{
registrationValidator.validate(registrationForm, result);
if(result.hasErrors())
{
return setupForm(modelAndView, registrationForm);
}
else
{
try
{
// ... Some non-transactional operations...
// The following operation is the only one annotated with #Transactional
// myExampleDao is #Autowired, can throw exception
myExampleDao.createFoo(bar);
// ... Other non-transactional operations...
return new ModelAndView("redirect:successful");
}
catch(Exception e)
{
throw new RegistrationException(e, registrationForm, result);
}
}
}
#ExceptionHandler(value = RegistrationException.class)
public ModelAndView registrationExceptionHandler(RegistrationException e) throws Exception
{
RegistrationForm registrationForm = e.getRegistrationForm();
BindingResult result = e.getBindingResult();
result.reject("exception", e.getMessage());
Map<String, Object> model = result.getModel();
return setupForm(new ModelAndView("registration", model), registrationForm);
}
private ModelAndView setupForm(ModelAndView modelAndView, RegistrationForm registrationForm) throws Exception
{
Map<String,Object> model = modelAndView.getModel();
model.put("currentYear", Calendar.getInstance().get(Calendar.YEAR));
return new ModelAndView("registration", model);
}
The problem I'm facing is that when the exception is thrown, the transaction is not rolled back.
Can anyone help?
Thank you.
Update: slightly changed the question for better understanding
Update: found a quite horrible solution to display the exception message in the view. Still facing the problem with the transaction that is not rolled back when the exception is thrown.
Update: I changed #Transactional to #Transactional(rollbackFor = Exception.class) in the MyExampleDao.createFoo(...) method and now everything is working perfectly. This solution is still ugly IMO, does anyone have a better solution?
Don't have transactions in your controllers. Put them in your service layer.
You can create an abstract controller class that implements exception handling like so (then each separate controller extends obviously) :
public class AbstractCtrl {
#Resource(name = "emailService")
private EmailService emailService;
/*
* Default exception handler, catchs all exceptions, redirects to friendly
* error page and send e-mail does not catch request mapping errors
*/
#ExceptionHandler(Exception.class)
public String myExceptionHandler(final Exception e) {
final StringWriter sw = new StringWriter();
final PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
final String strStackTrace = sw.toString(); // stack trace as a string
emailService.sendAlertMail(strStackTrace);
return "exception"; // default friendly excpetion message for user
}
}
But DO NOT put transaction in your controllers, put them in Service layer classes.
No one suggested a better solution than my ugly one. Here is my solution that solved the problem I had:
#RequestMapping(value = "/registration", method = RequestMethod.POST)
public ModelAndView submitForm(#ModelAttribute("registrationForm") RegistrationForm registrationForm,
BindingResult result,
ModelAndView modelAndView,
HttpServletRequest request) throws Exception
{
registrationValidator.validate(registrationForm, result);
if(result.hasErrors())
{
return setupForm(modelAndView, registrationForm);
}
else
{
try
{
// ... Some non-transactional operations...
// The following operation is the only one annotated with #Transactional
// myExampleDao is #Autowired, can throw exception
myExampleDao.createFoo(bar);
// ... Other non-transactional operations...
return new ModelAndView("redirect:successful");
}
catch(Exception e)
{
throw new RegistrationException(e, registrationForm, result);
}
}
}
#ExceptionHandler(value = RegistrationException.class)
public ModelAndView registrationExceptionHandler(RegistrationException e) throws Exception
{
RegistrationForm registrationForm = e.getRegistrationForm();
BindingResult result = e.getBindingResult();
result.reject("exception", e.getMessage());
Map<String, Object> model = result.getModel();
return setupForm(new ModelAndView("registration", model), registrationForm);
}
private ModelAndView setupForm(ModelAndView modelAndView, RegistrationForm registrationForm) throws Exception
{
Map<String,Object> model = modelAndView.getModel();
model.put("currentYear", Calendar.getInstance().get(Calendar.YEAR));
return new ModelAndView("registration", model);
}
The only way you have to manage transaction in a controller scope, open a session from the sessionFactory.
Session session = sessionFactory.openSession();
try{
Transaction tx = session.beginTransaction();
// code
session.save(foo);
tx.commit();
}catch(Exception e){
tx.rollback();
}finally{
try{session.close();}finally{}
}

Resources