Spring tags form do not show validation errors in jsp - spring

I am writing a page with user registration. Faced a display problem in the form of validation errors
My Controller:
#Controller
public class UIController {
private final Logger log = LoggerFactory.getLogger(getClass());
#Autowired
private UserService service;
#Autowired
private SecurityService securityService;
#Autowired
private UserValidator validator;
#PostMapping(value = "/login")
public String signIn(#ModelAttribute("userForm") UserTo userForm, BindingResult bindingResult, Model model) {
validator.validate(userForm, bindingResult);
User user = service.findByLogin(UserUtil.createNewFromTo(userForm).getLogin());
if (!userForm.getPassword().equals(user.getPassword())) {
log.info("invalid password {}", user);
return "redirect:/login";
}
log.info("signIn {}", user);
securityService.autologin(user.getLogin(), user.getPassword());
return "redirect:/welcome";
}
}
My Validator:
#Component
public class UserValidator implements Validator {
#Autowired
private UserRepositoryImpl userRepository;
#Override
public boolean supports(Class<?> aClass) {
return UserTo.class.equals(aClass);
}
#Override
public void validate(Object o, Errors errors) {
UserTo user = (UserTo) o;
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "login", "NotEmpty");
if (user.getLogin().length() < 6 || user.getLogin().length() > 32) {
errors.rejectValue("login", "Size.userForm.login");
}
if (userRepository.findByLogin(user.getLogin()) != null) {
errors.rejectValue("login", "Duplicate.userForm.login");
}
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "password", "NotEmpty");
if (user.getPassword().length() < 8 || user.getPassword().length() > 32) {
errors.rejectValue("password", "Size.userForm.password");
}
}
}
My jsp form :
<div id="wrapper">
<!--SLIDE-IN ICONS-->
<div class="user-icon"></div>
<div class="pass-icon"></div>
<!--END SLIDE-IN ICONS-->
<!--LOGIN FORM-->
<form:form name="login-form" modelAttribute="userForm" class="login-form" method="post" id="form">
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
<!--CONTENT-->
<div class="content">
<!--USERNAME-->
<spring:bind path="login">
<div class="form-group ${status.error ? 'has-error' : ''}">
<form:input name="login" id="login" type="text" path="login" class="input username"
placeholder="Login"></form:input>
<form:errors path="login"></form:errors>
</div>
</spring:bind>
<!--END USERNAME-->
</div>
<!--END CONTENT-->
<!--FOOTER-->
<div class="footer">
<button class="button" type="submit">Login</button>
<button class="register" type="button" name="submit" onclick=" register_url('${contextPath}/registration')">
Register
</button>
</div>
<!--END FOOTER-->
</form:form>
</div>
When debugging in chrome when sending a POST request, a 302 HTTP error appears.
Accordingly, if I set a breakpoint in the controller, then the debug is not processed.
Tell me what could be the problem?

The "form" tag in your jsp does not have "action" attribute.

You should not redirect on errors, because the redirect will erase the binding result. Instead do :
if (!userForm.getPassword().equals(user.getPassword())) {
log.info("invalid password {}", user);
return "login";
}

Related

Request method 'GET' not supported Spring Boot

I'm a beginer full stack developer. I need help.
I have the following code:
Controller:
#Controller
public class GreetingController {
#Autowired
private MessageRepos messageRepos;
#GetMapping("/")
public String greeting(Map<String, Object> model) {
return "greeting";
}
#GetMapping("/main")
public String main(Map<String, Object> model) {
Iterable<Message> messages = messageRepos.findAll();
model.put("messages", messages);
return "main";
}
#PostMapping("/main")
public String add(#RequestParam String text, #RequestParam String tag, Map<String, Object> model) {
Message message = new Message(text, tag);
messageRepos.save(message);
Iterable<Message> messages = messageRepos.findAll();
model.put("messages", messages);
return "main";
}
#PostMapping("filter")
public String filter(#RequestParam String filter, Map<String, Object> model) {
Iterable<Message> messages;
if (filter != null && !filter.isEmpty()) {
messages = messageRepos.findByTag(filter);
} else {
messages = messageRepos.findAll();
}
model.put("messages", messages);
return "main";
}
HTML:
</div>
<div>
<form method="post">
<input type="text" name="text" placeholder="Message" />
<input type="text" name="tag" placeholder="Tag">
<button type="submit">Add</button>
</form>
</div>
<div>
<form method="post" action="filter">
<input type="text" name="filter">
<button type="submit">Find</button>
</form>
</div>
When I click on the buttons add and find I get the problem.
Problem:
Request method 'GET' not supported.

form:errors are not displaying errors on JSP in Spring

I can not get the validation error messages to be showed in JSP page. The validation is working but messages are not getting displayed at all.
SystemValidator class :
#Override
public void validate(Object target, Errors errors) {
SystemUser systemUser = (SystemUser) target;
SystemUser user = systemUserRepository.findByUsername(systemUser.getUsername());
if (user != null && !(user.getId() == systemUser.getId()) || superUserName.equalsIgnoreCase(systemUser.getUsername())) {
errors.rejectValue("username", "Duplicate Username");
}
}
the view :
<form:form method="post" modelAttribute="user" action="addUser">
<div class="form-group">
<label for="username" class="col-form-label">User Name</label>
<form:input type="text" required="required" class="form-control" id="username" path="username"/>
<form:errors path = "username" cssClass = "error" />
</div>
<form:input type="hidden" id="id" name="id" path="id"/>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</form:form>
the controller class :
#RequestMapping(value = "/addUser", method = RequestMethod.POST)
public String saveUser(Model model, #Validated #ModelAttribute("user") SystemUser user, BindingResult result) {
logger.info("User save/update request received [{}]", user.toString());
if (result.hasErrors()) {
loadData(model);
model.addAttribute("user", user);
return "users";
}
systemUserService.saveSystemUser(user);
loadData(model);
return "users";
}
private void loadData(Model model) {
model.addAttribute("users", systemUserService.getAllUsers());
model.addAttribute("user", new SystemUser());
}
Use #Valid instead of #Validated like below.
#RequestMapping(value = "/addUser", method = RequestMethod.POST)
public String saveUser(#Valid #ModelAttribute("user") SystemUser user, BindingResult result,Model model)
Don't do model.addAttribute("user", new SystemUser()); when validation error occurs.
Change to this:
if (result.hasErrors()) {
loadData(model, user);
return "users";
}
And:
private void loadData(Model model, User user) {
model.addAttribute("users", systemUserService.getAllUsers());
model.addAttribute("user", user);
}

Spring #ModelAttributes auto binding

Hi i am tring to use #ModelAttributes for auto binding from view to controller.
this jsp page should send all elements which is Personal Schedule VO.
<form name="insertFrm" action="myScheduleInsert" method="POST">
<ul>
<input type="hidden" class="form-control" name="perschd_num" >
<li>
<label class='control-label'>title</label>
<input type="text" class="form-control" name="perschd_title" >
</li>
<li>
<input type="hidden" class="form-control" name="perschd_writer" >
<li>
<label class='control-label'>start date</label>
<input type="date" id="fullYear" class="form-control" name="perschd_start_date" value="">
</li>
<li>
<label class='control-label'>end date</label>
<input type="date" class="form-control" name="perschd_end_date" >
</li>
<li>
<label class='control-label'>content</label>
<input type="text" class="form-control" name="perschd_cont" >
</li>
</ul>
</div>
<!-- button-->
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<input type="submit" class="btn btn-primary">
</form>
and this is my controller for insert feature by using #ModelAttributes PerschdVO perschd and when i try print all out, all is null.
//insertSchedule
#RequestMapping("/myScheduleInsert")
public String myScheduleInsert(#ModelAttribute PerschdVO perschdVO ){
String view="redirect:/professor/mypage/mySchedule";
*System.out.println(perschdVO);*
try {
proService.insertPerschd(perschdVO);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return view;
}
and this is PerschdVO
public class PerschdVO {
private int perschd_num;
private String perschd_title;
private String perschd_cont;
private Date perschd_date;
private String perschd_start_date;
private String perschd_end_date;
private String perschd_writer; //id
public String getPerschd_writer() {
return perschd_writer;
}
public void setPerschd_writer(String perschd_writer) {
this.perschd_writer = perschd_writer;
}
public int getPerschd_num() {
return perschd_num;
}
public void setPerschd_num(int perschd_num) {
this.perschd_num = perschd_num;
}
public String getPerschd_title() {
return perschd_title;
}
public void setPerschd_title(String perschd_title) {
this.perschd_title = perschd_title;
}
public String getPerschd_cont() {
return perschd_cont;
}
public void setPerschd_cont(String perschd_cont) {
this.perschd_cont = perschd_cont;
}
public Date getPerschd_date() {
return perschd_date;
}
public void setPerschd_date(Date perschd_date) {
this.perschd_date = perschd_date;
}
public String getPerschd_start_date() {
return perschd_start_date;
}
public void setPerschd_start_date(String perschd_start_date) {
this.perschd_start_date = perschd_start_date;
}
public String getPerschd_end_date() {
return perschd_end_date;
}
public void setPerschd_end_date(String perschd_end_date) {
this.perschd_end_date = perschd_end_date;
}
why this elements from jsp does not match with this? Eventhough i matched all name in jsp file to PerschdVO's get() names.
and if i fix like this
public String myScheduleInsert(
Principal who,
#RequestParam("perschd_start_date")String perschd_start_date,
#RequestParam("perschd_end_date")String perschd_end_date,
#RequestParam("perschd_title")String perschd_title,
#RequestParam("perschd_cont")String perschd_cont
){
PerschdVO perschdVO = new PerschdVO();
...}
than the data from jsp could be sent to #Controller, but still can't get informations about using #ModelAttributes. cos if i use that always 400 bad request happened.

spring-security:HTTP Status 405 - Request method 'POST' not supported

I have checked so many answers here for the same but it seems nothing worked for me. I have spring security with spring mvc. when my user is trying to sign up I am sending post data to my controller. but it is giving me 405 post not supported I have disabled csrf token in security config. please let me know where did I go wrong?
Here's my webSecurityConfigureDapter:
package org.pkb.springlogin.config;
import org.pkb.springlogin.authentication.MyDBAuthenticationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
#Configuration
// #EnableWebSecurity = #EnableWebMVCSecurity + Extra features
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
MyDBAuthenticationService myDBAauthenticationService;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// Users in memory.
auth.inMemoryAuthentication().withUser("user1").password("12345").roles("USER");
auth.inMemoryAuthentication().withUser("admin1").password("12345").roles("USER, ADMIN");
// For User in database.
auth.userDetailsService(myDBAauthenticationService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
// The pages does not require login
http.authorizeRequests().antMatchers("/", "/welcome", "/login", "/logout","/signUp").permitAll();
// /userInfo page requires login as USER or ADMIN.
// If no login, it will redirect to /login page.
http.authorizeRequests().antMatchers("/userInfo").access("hasAnyRole('ROLE_USER', 'ROLE_ADMIN')");
// For ADMIN only.
http.authorizeRequests().antMatchers("/admin").access("hasRole('ROLE_ADMIN')");
// When the user has logged in as XX.
// But access a page that requires role YY,
// AccessDeniedException will throw.
http.authorizeRequests().and().exceptionHandling().accessDeniedPage("/403");
// Config for Login Form
http.authorizeRequests().and().formLogin()//
// Submit URL of login page.
.loginProcessingUrl("/j_spring_security_check") // Submit URL
.loginPage("/login")//
.defaultSuccessUrl("/userInfo")//
.failureUrl("/login?error=true")//
.usernameParameter("username")//
.passwordParameter("password")
// Config for Logout Page
.and().logout().logoutUrl("/logout").logoutSuccessUrl("/logoutSuccessful");
}
}
Here's my sign up page
<%# taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%#taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%# taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%# page isELIgnored="false"%>
<c:set var="contextPath" value="${pageContext.request.contextPath}"/>
<html lang="en">
<head>
<link rel="stylesheet" href="<c:url value="/resources/css/bootstrap-theme.min.css"/>">
<link rel="stylesheet" href="<c:url value="/resources/css/bootstrap.min.css"/>">
<title>Sign Up Form</title>
<meta http-equiv="Content-Type" content="text/html charset=UTF-8" />
</head>
<body>
<div class="jumbotron page-header">
<h2>Login</h2>
</div>
<form:form class="form-horizontal" method="post"
name="userReg" id="userReg" modelAttribute="userForm" action="${contextPath}/login">
<div class="container">
<label class="col-sm-2 control-label">Name</label>
<div class="col-sm-4">
<input name="userName" type="text" class="form-control" id="userName" placeholder="Name" />
</div>
</div>
<br>
<div class="container">
<label class="col-sm-2 control-label">Email</label>
<div class="col-sm-4">
<input name="email" class="form-control" id="email" placeholder="Email" />
</div>
</div>
<br>
<div class="container">
<label class="col-sm-2 control-label">Date of Birth(dd-mm-yyyy)</label>
<div class="col-sm-4">
<input name="dob" type="text" class="form-control" id="dob" placeholder="Date of birth" />
</div>
</div>
<br>
<div class="container">
<label class="col-sm-2 control-label">Password</label>
<div class="col-sm-4">
<input name="password" type="password" class="form-control" id="password" placeholder="password" />
</div>
</div>
<br>
<div class="container">
<label class="col-sm-2 control-label">Confirm Password</label>
<div class="col-sm-4">
<input name="confirmPassword" type="password" class="form-control" id="cpassword" placeholder="confirm password" />
<span id='message'></span>
</div>
</div>
<br>
<div class="container">
<label class="col-sm-2 control-label">User type</label>
<div class="col-sm-4">
<select class="form-control" name="type" >
<option selected="selected">--select--</option>
<option value="user" >User</option>
<option value="admin">Admin</option>
</select>
</div>
</div>
<br>
<br>
<div class="col-md-6 center-block">
<input type="submit" class="btn-lg btn-primary center-block" value="save">
</div>
</form:form>
</body>
<script type = "text/javascript"
src = "https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/jquery.validation/1.15.1/jquery.validate.min.js"></script>
<script src="<c:url value="/resources/js/form-validation.js"/>"></script>
<script src="<c:url value="/resources/js/passwordVerification.js"/>"></script>
</html>
Here's my MainController
package org.pkb.springlogin.controller;
import java.security.Principal;
import org.pkb.springlogin.manager.SignUpHandler;
import org.pkb.springlogin.model.SignUpInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
#Controller
public class MainController {
#Autowired
SignUpHandler signupHandler;
private static final Logger logger =LoggerFactory.getLogger(MainController.class);
#RequestMapping(value = { "/", "/welcome" }, method = RequestMethod.GET)
public String welcomePage(Model model) {
model.addAttribute("title", "Welcome");
model.addAttribute("message", "Hello friend!");
return "welcomePage";
}
#RequestMapping(value = "/admin", method = RequestMethod.GET)
public String adminPage(Model model) {
return "adminPage";
}
#RequestMapping(value="/signUp",method=RequestMethod.POST)
public String userLogin(#ModelAttribute("userForm") SignUpInfo user,ModelMap model){
System.out.println(user);
Integer id=signupHandler.process(user);
if(id!=null){
logger.debug("ID in controller:"+id);
return "success";
}
logger.error("error in controller");
return "Failure";
}
#RequestMapping(value="/signUp",method=RequestMethod.GET)
public String register(Model model){
SignUpInfo user=new SignUpInfo();
model.addAttribute("userForm", user);
return "signUp";
}
#RequestMapping(value = "/login", method = RequestMethod.GET)
public String loginPage(Model model ) {
return "loginPage";
}
#RequestMapping(value = "/logoutSuccessful", method = RequestMethod.GET)
public String logoutSuccessfulPage(Model model) {
model.addAttribute("title", "Logout");
return "logoutSuccessfulPage";
}
#RequestMapping(value = "/userInfo", method = RequestMethod.GET)
public String userInfo(Model model, Principal principal) {
// After user login successfully.
String userName = principal.getName();
System.out.println("User Name: "+ userName);
return "userInfoPage";
}
#RequestMapping(value = "/403", method = RequestMethod.GET)
public String accessDenied(Model model, Principal principal) {
if (principal != null) {
model.addAttribute("message", "Hi " + principal.getName()
+ "<br> You do not have permission to access this page!");
} else {
model.addAttribute("msg",
"You do not have permission to access this page!");
}
return "403Page";
}
}
Here's my signUpInfo
package org.pkb.springlogin.model;
public class SignUpInfo {
private String userName;
private String password;
private String confirmPassword;
private Type type;
private Byte enabled;
public Byte getEnabled() {
return enabled;
}
public void setEnabled(Byte enabled) {
this.enabled = enabled;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getConfirmPassword() {
return confirmPassword;
}
public void setConfirmPassword(String confirmPassword) {
this.confirmPassword = confirmPassword;
}
public Type getType() {
return type;
}
public void setType(Type type) {
this.type = type;
}
#Override
public String toString() {
return "SignUpInfo [userName=" + userName + ", password=" + password + ", confirmPassword=" + confirmPassword
+ ", type=" + type + "]";
}
}
Form is POST-ed to /login
action="${contextPath}/login"
but login is annotated to support only GET
#RequestMapping(value = "/login", method = RequestMethod.GET)
public String loginPage(Model model ) {
return "loginPage";
}
maybe you should post to /signUp

Spring Boot multiple controllers with same mapping

My problem is very similar with this one: Spring MVC Multiple Controllers with same #RequestMapping
I'm building simple Human Resources web application with Spring Boot. I have a list of jobs and individual url for each job:
localhost:8080/jobs/1
This page contains job posting details and a form which unauthenticated users -applicants, in this case- can use to apply this job. Authenticated users -HR Manager-, can see only posting details, not the form. I have trouble with validating form inputs.
What I tried first:
#Controller
public class ApplicationController {
private final AppService appService;
#Autowired
public ApplicationController(AppService appService) {
this.appService = appService;
}
#RequestMapping(value = "/jobs/{id}", method = RequestMethod.POST)
public String handleApplyForm(#PathVariable Long id, #Valid #ModelAttribute("form") ApplyForm form, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "job_detail"; //HTML page which contains job details and the application form
}
appService.apply(form, id);
return "redirect:/jobs";
}
#RequestMapping(value = "/applications/{id}", method = RequestMethod.GET)
public ModelAndView getApplicationPage(#PathVariable Long id) {
if (null == appService.getAppById(id)) {
throw new NoSuchElementException(String.format("Application=%s not found", id));
} else {
return new ModelAndView("application_detail", "app", appService.getAppById(id));
}
}
}
As you guess this didn't work because I couldn't get the models. So I put handleApplyForm() to JobController and changed a little bit:
#Controller
public class JobController {
private final JobService jobService;
private final AppService appService;
#Autowired
public JobController(JobService jobService, AppService appService) {
this.jobService = jobService;
this.appService = appService;
}
#RequestMapping(value = "/jobs/{id}", method = RequestMethod.POST)
public ModelAndView handleApplyForm(#PathVariable Long id, #Valid #ModelAttribute("form") ApplyForm form, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return getJobPage(id);
}
appService.apply(form, id);
return new ModelAndView("redirect:/jobs");
}
#RequestMapping(value = "/jobs/{id}", method = RequestMethod.GET)
public ModelAndView getJobPage(#PathVariable Long id) {
Map<String, Object> model = new HashMap<String, Object>();
if (null == jobService.getJobById(id)) {
throw new NoSuchElementException(String.format("Job=%s not found", id));
} else {
model.put("job", jobService.getJobById(id));
model.put("form", new ApplyForm());
}
return new ModelAndView("job_detail", model);
}
}
With this way, validations works but I still can't get the same effect here as it refreshes the page so that all valid inputs disappear and error messages don't appear.
By the way, job_detail.html is like this:
<h1>Job Details</h1>
<p th:inline="text"><strong>Title:</strong> [[${job.title}]]</p>
<p th:inline="text"><strong>Description:</strong> [[${job.description}]]</p>
<p th:inline="text"><strong>Number of people to hire:</strong> [[${job.numPeopleToHire}]]</p>
<p th:inline="text"><strong>Last application date:</strong> [[${job.lastDate}]]</p>
<div sec:authorize="isAuthenticated()">
<form th:action="#{/jobs/} + ${job.id}" method="post">
<input type="submit" value="Delete this posting" name="delete" />
</form>
</div>
<div sec:authorize="isAnonymous()">
<h1>Application Form</h1>
<form action="#" th:action="#{/jobs/} + ${job.id}" method="post">
<div>
<label>First name</label>
<input type="text" name="firstName" th:value="${form.firstName}" />
<td th:if="${#fields.hasErrors('form.firstName')}" th:errors="${form.firstName}"></td>
</div>
<!-- and other input fields -->
<input type="submit" value="Submit" name="apply" /> <input type="reset" value="Reset" />
</form>
</div>
Check thymeleaf documentation here
Values for th:field attributes must be selection expressions (*{...}),
Also ApplyForm is exposed then you can catch it in the form.
Then your form should looks like this:
<form action="#" th:action="#{/jobs/} + ${job.id}" th:object="${applyForm}" method="post">
<div>
<label>First name</label>
<input type="text" name="firstName" th:value="*{firstName}" />
<td th:if="${#fields.hasErrors('firstName')}" th:errors="*{firstName}"></td>
</div>
<!-- and other input fields -->
<input type="submit" value="Submit" name="apply" /> <input type="reset" value="Reset" />
</form>

Resources