Can someone please tell me why i am getting error:HTTP Status 405 – Method Not Allowed ?
I am trying accomplish that after method doPost() ,user will be redirected to "/logout" controller ,where is invalidate session.
It's funny because method is called ,do everything what should do(update user in database), but after send to user error 405.Another where i use doPost() (for example: LoginController) working well ,but when i try compere and find bug ,i dont see any :<
<div class="container">
<div class="col-md-8 col-md-offset-2">
<form method="post" action="account">
<div class="form-group">
<label for="email">Email address</label>
<input name="email" type="email" class="form-control" id="email"
value="${sessionScope.loggedUser.email}" required aria-describedby="emailHelp"
placeholder="Enter email">
</div>
<div class="form-group">
<label for="password">Password</label>
<input name="password" type="password" minlength="5" maxlength="40" required class="form-control"
id="password" placeholder="Password">
</div>
<div class="form-group">
<label for="repeatPassword">Repeat Password</label>
<input name="repeatPassword" type="password" minlength="5" maxlength="40" required class="form-control"
id="repeatPassword" placeholder="Password">
</div>
<input class="btn btn-lg btn-primary btn-block" type="submit" value="Save changes"/>
</form>
</div>
</div>
#WebServlet("/account")
public class AccountController extends HttpServlet {
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String email = req.getParameter("email");
String password = req.getParameter("password");
String repeatPassword = req.getParameter("repeatPassword");
if (email == null || password == null || repeatPassword == null) {
doGet(req, resp);
return;
}
if (password.equals(repeatPassword)) {
HttpSession session = req.getSession();
User user = (User) session.getAttribute("loggedUser");
user.setEmail(email);
String sha1hexPassword = DigestUtils.sha1Hex(password);
user.setPassword(sha1hexPassword);
UserService service = new UserService();
try {
service.update(user);
} catch (UpdateObjectException e) {
e.printStackTrace();
}
req.getRequestDispatcher("/logout").forward(req, resp);
} else {
req.setAttribute("errorMessage", "Passwords not the same");
req.setAttribute("fragment", "account");
req.getRequestDispatcher("WEB-INF/index.jsp").forward(req, resp);
}
}
}
Thanks for any hint.
Your doGet() method call is inside the server doPost() code. You should redirect the response, doGet is for recieving a GET request and any query string.
Problem solved. Here:
req.getRequestDispatcher("/logout").forward(req, resp);
i should do
resp.sendRedirect(req.getContextPath()+"/logout");
because in "/logout" i have only doGet() method ,and if i use "getRequestDispatcher()" its try to find doPost() method.
Related
Im trying to use ModelAttribute annotation to bind thymeleaf form and model but whatever i do it still null and i've searched for days..
Controller method
#RequestMapping("/signup")
public String register(#ModelAttribute("request") RegistrationRequest request, HttpSession session, Model model) {
if (session.getAttribute("user") != null) {
User user = (User) session.getAttribute("user");
LOGGER.info("User {} redirected to home instead of signup because he was already logged", user.getUsername());
return "redirect:/";
}
model.addAttribute("request", new RegistrationRequest());
model.addAttribute("IS_SUCCESS", service.handleRegistrationRequest(request, session));
model.addAttribute("USERNAME_FIELD_ERROR", session.getAttribute("USERNAME_FIELD_ERROR"));
model.addAttribute("EMAIL_FIELD_ERROR", session.getAttribute("EMAIL_FIELD_ERROR"));
session.removeAttribute("USERNAME_FIELD_ERROR");
session.removeAttribute("EMAIL_FIELD_ERROR");
return "signup_step1";
}
HTML part
<form th:action="#{/signup}" method="post" th:object="${request}">
<div id="username-input-wrapper" class="input-wrapper" th:classappend="${USERNAME_FIELD_ERROR != null} ? 'input-wrapper-error' : ''">
<label for="username" th:text="#{signup.label.username}"></label>
<input id="username" class="form-control" type="text" name="username" th:field="*{username}" th:placeholder="#{signup.textfield.prompt.username}">
<span class="error-message" th:text="${USERNAME_FIELD_ERROR != null} ? #{${USERNAME_FIELD_ERROR}} : ''"></span>
</div>
<div id="email-input-wrapper" class="input-wrapper" th:classappend="${EMAIL_FIELD_ERROR != null} ? 'input-wrapper-error' : ''">
<label for="email" th:text="#{signup.label.email}"></label>
<input id="email" class="form-control" type="text" name="email" th:field="*{email}" th:placeholder="#{signup.textfield.prompt.email}">
<span class="error-message" th:text="${EMAIL_FIELD_ERROR != null} ? #{${EMAIL_FIELD_ERROR}} : ''"></span>
</div>
<div class="submit-box">
<p class="conditions">
By creating an account, you agree to our Terms and Privacy Policy.
</p>
<button class="signup-btn" type="submit" th:text="#{signup.button.continue}"></button>
</div>
</form>
I'm trying to develop an application in spring boot + thymeleaf, and I'm able to retrieve the logged in user details in the profile tab from the MySQL database, but when I try to change one or two field details (update) and hit the update button it is showing me an error message - Fri Sep 04 20:39:47 IST 2020
There was an unexpected error (type=Method Not Allowed, status=405).
Request method 'POST' not supported
see my controller code (I'm using #RestController annotated on top of the class)-
#RequestMapping(value = "/profile", method = RequestMethod.PUT)
public ModelAndView updateProfile(#ModelAttribute Customer customer, HttpSession session) {
ModelAndView model = new ModelAndView();
Customer exist = cRepo.findByCustEmail(customer.getCustEmail());
if(exist != null) {
if(exist.getCustEmail().equals(session.getAttribute("emailsession"))) {
cRepo.save(customer);
model.addObject("msg", "User Details has been successfully updated!!");
model.setViewName("profile");
}
}else {
model.addObject("exist", "Please enter correct email address!");
String email = (String) session.getAttribute("emailsession");
Customer cust = cRepo.findByCustEmail(email);
model.addObject("customer", cust);
model.setViewName("profile");
}
return model;
}
Thymleaf code (html) -
<div align="center" class="alert alert-success" th:if="${msg}" th:utext="${msg}"></div>
<div align="center" class="alert alert-danger" th:if="${exist}" th:utext="${exist}"></div>
<!-- Modal HTML -->
<div id="myModal">
<div class="modal-dialog modal-login">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Profile Details</h4>
</div>
<div class="modal-body">
<form name="myForm" th:action="#{/profile}" th:object="${customer}" method="post">
<div class="form-group">
<i class="fa fa-id-card"></i>
<input name="id" type="text" class="form-control" placeholder="Enter Id" th:field="${customer.custId}" disabled="true" required="required" />
</div>
<div class="form-group">
<i class="fa fa-user"></i>
<input name="name" type="text" class="form-control" placeholder="Enter Name" th:field="${customer.custName}" required="required" />
</div>
<div class="form-group">
<i class="fa fa-envelope"></i>
<input name="email" type="email" class="form-control" placeholder="Enter Email" th:field="${customer.custEmail}" required="required" />
</div>
<div class="form-group">
<i class="fa fa-lock"></i>
<input name="password" type="text" class="form-control" placeholder="Enter Password" th:field="${customer.custPassword}" required="required" />
</div>
<div class="form-group">
<input type="submit" class="btn btn-primary btn-block btn-lg" value="Update" />
</div>
</form>
</div>
</div>
</div>
</div>
I want when user login and visit he/she should be able to check his/her profile(which I'm able to do working code) and when the user wants to update few fields(1-2 based on choice) and hit update he/she should be able to update the details (not create new user or record) because when I use #Controller on top of class then this code work and create new user instead update.
Your controller is annotated with #RequestMapping(value = "/profile", method = RequestMethod.PUT) which makes it a PUT endpoint. However, your request is clearly a POST. If we look at your html form it contains method="post". HTML forms only support GET and POST as valid methods so you need to update your endpoint to be a POST endpoint.
tldr;
RequestMapping(value = "/profile", method = RequestMethod.PUT)
to
RequestMapping(value = "/profile", method = RequestMethod.POST)
You request mapping in is POST but Controller has set to accept request as PUT.
<form name="myForm" th:action="#{/profile}" th:object="${customer}" **method="post"**>
#RequestMapping(value = "/profile", method = **RequestMethod.PUT**)
Just keep these in similar way both should be same.
Please check what I find and resolve this.
#RequestMapping(value = "/profile" ,method = RequestMethod.POST)
public ModelAndView updateProfile(#ModelAttribute Customer customer, HttpSession session) {
ModelAndView model = new ModelAndView();
Customer exist = cRepo.findByCustEmail(customer.getCustEmail());
if(exist != null) {
if(exist.getCustEmail().equals(session.getAttribute("emailsession"))) {
**exist.setCustId(exist.getCustId());
exist.setCustName(customer.getCustName());
exist.setCustEmail(customer.getCustEmail());
exist.setCustPassword(customer.getCustPassword());**
cRepo.save(exist);
model.addObject("msg", "User Details has been successfully updated!!");
model.addObject("customer", exist);
model.setViewName("profile");
}
}else {
model.addObject("exist", "Please enter correct email address!");
String email = (String) session.getAttribute("emailsession");
Customer cust = cRepo.findByCustEmail(email);
model.addObject("customer", cust);
model.setViewName("profile");
}
return model;
}
How to display this message from custom class type annotation in Spring as a warning message in HTML form? I am using Spring Boot MVC and Thymeleaf. Thymeleaf can handle field errors and global errors that are not associated with any specific fields in the form, but still exist. If this custom annotation finds that passwords don't match is this global error or field error? How can I show this message?
Custom annotation:
#Target({ TYPE, ANNOTATION_TYPE })
#Retention(RUNTIME)
#Constraint(validatedBy = PasswordMatchesValidator.class)
#Documented
public #interface PasswordMatches {
String message() default "Passwords don't match";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Validator class:
public class PasswordMatchesValidator implements ConstraintValidator<PasswordMatches, Object> {
#Override
public void initialize(PasswordMatches passwordMatches) {
}
#Override
public boolean isValid(Object obj, ConstraintValidatorContext context) {
UserDto userDto = (UserDto) obj;
return userDto.getPassword().equals(userDto.getMatchingPassword());
}
}
Post Controller
#PostMapping(value = "/register")
public ModelAndView processRegistration(ModelAndView modelAndView, #Valid #ModelAttribute ("userDto") UserDto userDto,
BindingResult bindingResult, HttpServletRequest request, Errors errors) {
// Lookup user in database by e-mail
User userExists = userService.findByEmail(userDto.getEmail());
System.out.println(userExists);
if (userExists != null) {
modelAndView.addObject("alreadyRegisteredMessage", "Oops! There is already a user registered with the email provided.");
modelAndView.setViewName("register");
bindingResult.reject("email");
}
if (bindingResult.hasErrors()) {
modelAndView.setViewName("register");
} else { // new user so we create user and send confirmation e-mail
User user = userService.createNewAccount(userDto);
user.setEnabled(false);
userService.saveUser(user);
modelAndView.addObject("confirmationMessage", "A confirmation e-mail has been sent to " + userDto.getEmail());
modelAndView.setViewName("registered");
}
}
HTML form:
<div class="container">
<div class="row">
<div class="card col-6 mx-auto" style="width: 20rem; margin: 20px;">
<div class="card-body">
<h4 class="card-title">Registration form:</h4>
<form th:action="#{register}" th:object="${userDto}" th:method="post" method="post" action="register">
<div class="form-group">
<label th:for="name" for="Name">Name</label>
<input type="text" class="form-control" th:field="*{name}" id="name" placeholder="Enter name">
<p class="text-danger" th:if="${#fields.hasErrors('name')}" th:errors="*{name}"></p>
</div>
<div class="form-group">
<label th:for="surname" for="Surname">Surname</label>
<input type="text" class="form-control" th:field="*{surname}" id="surname" placeholder="Enter surname">
<p class="text-danger" th:if="${#fields.hasErrors('surname')}" th:errors="*{surname}"></p>
</div>
<div class="form-group">
<label th:for="username" for="Username">Username</label>
<input type="text" class="form-control" th:field="*{username}" id="username" placeholder="Enter username">
<p class="text-danger" th:if="${#fields.hasErrors('username')}" th:errors="*{username}"></p>
</div>
<div class="form-group">
<label th:for="email" for="Email">Email address</label>
<input type="email" class="form-control" th:field="*{email}" id="email" aria-describedby="emailHelp" placeholder="Enter email">
<small id="emailHelp" class="form-text text-muted">We'll never share your email with anyone else.</small>
<p class="text-danger"th:if="${#fields.hasErrors('email')}" th:errors="*{email}"></p>
<p class="text-danger" th:text="${alreadyRegisteredMessage}"></p>
</div>
<div class="form-group">
<label th:for="password" for="Password">Password</label>
<input type="password" class="form-control" th:field="*{password}" id="password" placeholder="Password">
<p class="text-danger"th:if="${#fields.hasErrors('password')}" th:errors="*{password}"></p>
</div>
<div class="form-group">
<label th:for="matchingPassword" for="matchingPassword">Confirm Password</label>
<input type="password" class="form-control" th:field="*{matchingPassword}" id="matchingPassword" placeholder="Password">
<p class="text-danger"th:if="${#fields.hasErrors('matchingPassword')}" th:errors="*{matchingPassword}"></p>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
</div>
</div>
</div>
You can customize the PasswordMatchesValidator class to display the warning message.
public class PasswordMatchesValidator implements ConstraintValidator<PasswordMatches, Object> {
private String message;
#Override
public void initialize(PasswordMatches passwordMatches) {
this.message = passwordMatches.message();
}
#Override
public boolean isValid(Object obj, ConstraintValidatorContext context) {
final UserDto userDto = (UserDto) obj;
boolean isValid = userDto.getPassword().equals(userDto.getMatchingPassword());
if (!isValid) {
context.disableDefaultConstraintViolation();
context
.buildConstraintViolationWithTemplate( message )
.addPropertyNode( "matchingPassword" ).addConstraintViolation();
}
return isValid;
}
}
The solution is to include global errors Thymeleaf syntax in HTML form. But for some reason single quote in this message: "Passwords don't match", doesn't get rendered. The answer to this problem is here.
I have a web application in which a specific url '/newPost' should only be accessible to one user, the admin. When trying to navigate to '/newPost', the user should be redirected to a login page where he must verify his identity as admin. This all works, except for when the user fills out the login form, the form gets posted to '/login' everytime. I am currently at a loss as to why it is posting to '/login' instead of the path I redirect to using thyme leafs: th:action="#{/newPost}"
TLDR; I keep getting redirected to /login after submitting login form. I am using Spring boot, Spring security, and thymeleaf.
Controller:
/*Keeps getting called*/
#RequestMapping(value="/login", method=RequestMethod.GET)
public String login(Model model)
{
model.addAttribute("lc", new LoginCredentials());
System.out.println("Login controller");
return "login";
}
/*RequestMapping I want to be called*/
#RequestMapping(value="/newPost", method = RequestMethod.GET)
public String isLoggedIn(#ModelAttribute LoginCredentials lc, Model model)
{
if(lc.isAdmin())
{
System.out.println("lc is admin");
model.addAttribute("bp",new BlogPost());
return "newPost";
} else
{
System.out.println("lc is not admin");
return "login";
}
}
Login Form:
<form class="form-signin" th:action="#{/newPost}" th:object="${lc}" method = "post">
<h2 class="form-signin-heading">Please sign in</h2>
<label for="inputEmail" class="sr-only">Username</label>
<input type="text" id="username" class="form-control" th:field="*{inputUsername}" placeholder="Username" required="required" autofocus="autofocus" />
<label for="inputPassword" class="sr-only">Password</label>
<input type="password" id="password" th:field="*{inputPsswd}" class="form-control" placeholder="Password" required ="required" />
<button class="btn btn-lg btn-primary btn-block" type="submit" style="background-color:#F6358A;">Sign in</button>
</form>
Security Configuration:
httpSecurity
.authorizeRequests()
.antMatchers("/","/videos","/BlogPost","/index","/aboutUs").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll();
What's your login page jsp name? Is that "login.jsp"?
You login method return is "login", that means it will return to login.jsp.
User return "/newPost" instead.
My above question was a mess. This was my first time trying to work with Java Spring and my question shows. I hope this explanation helps a future user.
Firstly:
The action should not be different from /login. I was essentially causing an infinite loop of logins because I was sending the user to /newPost by submitted the login form, but they could not access the /newPost until they provided the correct credentials. Spring dutifully redirected the user to /login in order to provide the correct credentials, repeating the process.
This:
th:action="#{/newPost}"
should be:
th:action="#{/login}"
with a corresponding RequestMapping like so:
#RequestMapping(value="/login", method=RequestMethod.POST)
public String loginPost(Model model)
{
//do foo
}
Secondly:
I attempted to do Spring Security's job for it.
if(lc.isAdmin())
{
System.out.println("lc is admin");
model.addAttribute("bp",new BlogPost());
return "newPost";
} else
{
System.out.println("lc is not admin");
return "login";
}
Since I only needed security for a single user, I should have configured an AuthenticationManagerBuilder object in my security configuration like so:
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth)
{
try
{
auth
.inMemoryAuthentication()
.withUser("admin")
.password("password")
.roles("ADMIN");
} catch (Exception e) {
e.printStackTrace();
}
}
Thirdly:
Since I changed Springs global configuration, I should not pass in an object to login.html. The new form should use input fields like so:
<form class="form-signin" th:action="#{/login}" method = "post">
<h2 class="form-signin-heading">Please sign in</h2>
<label for="inputEmail" class="sr-only">Username</label>
<input type="text" id="username" name="username" class="form-control" placeholder="Username" required="required" autofocus="autofocus" />
<label for="inputPassword" class="sr-only">Password</label>
<input type="password" id="password" name="password" class="form-control" placeholder="Password" required ="required" />
<button class="btn btn-lg btn-primary btn-block" type="submit" style="background-color:#F6358A;">Sign in</button>
</form>
i was making a simple page with a formulary using bootstrap and Eclipse (With JBoss).
Html code(Just < body > part):
<div class="jumbotron">
<div class="container">
<form method="POST" class="form-inline" action="validarIngresoAdmin.htm">
<h4>Ingrese sus datos:</h4>
<input type="text" class="form-control input-sm" placeholder="Email" name="txtEmail">
<input type="password" class="form-control input-sm" placeholder="Password" name="txtPsw">
<button type="submit" class="btn btn-danger">Iniciar sesión</button>
</form>
</div>
but when i try to get the attributes txtEmail and txtPsw they return null.
Eclipse code:
private void doPost (HttpServletRequest request, HttpServletResponse response) throws IOException
{
PrintWriter pw = response.getWriter();
String email = (String)request.getAttribute("txtEmail");
String pass = (String)request.getAttribute("txtPsw");
}
¿Why 'email' and 'pass' attributes return null?
P.S: Sorry about my english.
Thanks.
To retrieve http variables, you should be using getParameter rather than getAttribute.