BindingResult isn't detecting JSR:303 annotation based errors - java

I am new to Spring MVC. I am trying a simple application to validate form values. I am use Spring BindingResult and JSR303 for field validation. But for some reason the validation errors don't show up in the error tag. In fact, the binding result doesn't return any errors.
My bean is as follows:
package com.app.ebl.bean.login;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
public class LoginBean {
#NotNull(message = "User Name field can not be blank")
#Size(max = 10, message = "User Name should not be more than 10 characters")
private String userName;
private String password;
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;
}
}
My Controller Class Look like below,
package com.app.ebl.controller.login;
import javax.validation.Valid;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.app.ebl.bean.login.LoginBean;
#Controller
public class LoginController {
private static final Log log = LogFactory.getLog(LoginController.class);
#RequestMapping(value="/login", method=RequestMethod.GET)
public String toLogin(Model model)
{
model.addAttribute("login",new LoginBean());
return "login";
}
#RequestMapping(value="/authenticate", method=RequestMethod.POST)
public String authenticate(#ModelAttribute(value = "login") #Valid LoginBean login,
BindingResult bindingResult, Model model)
{
if(bindingResult.hasErrors()) {
return "login";
}
model.addAttribute("userName",login.getUserName());
return "home";
}
}
Now If I provide no value in the user name field, system is not validating the same, and allowing me to the next view.
Can someone please help me.
Thanks in Advance,

The #Size annotation accepts both min and max parameter. When min is not provided, it uses default:
/**
* #return size the element must be higher or equal to
*/
int min() default 0;
You didn't provided any, and my guess is that your controller do not transform empty strings to null. Firstly, I'd switch to #NotBlank annotation from org.hibernate.validator. In addition to #NotNull, it removes trailing whtespaces:
public class LoginBean {
#NotBlank(message = "User Name field can not be blank")
#Size(max = 10, message = "User Name should not be more than 10 characters")
private String userName;
....
Additionally, define init binder in your controller, that will change empty strings to null with help of StringTrimmerEditor from org.springframework.beans.propertyeditors.StringTrimmerEditor
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(String.class, new StringTrimmerEditor(true));
}

Took me a while to figure this out, I hope this helps someone.
I had the same problem. The bean just wasn't getting validated.
What I had done was created a library via the "buildpath" menu and added the hibernate-validator.jar to my library. I could use the hibernate annotations and I wasn't getting any compile errors but when I ran it, the beans never got validated.
The reason was that the application needed the hibernate-validator.jar in WEB-INF/lib folder, so that it could use it for validation.

Related

Error 405: Method not Allowed on DELETE and PUT

I followed this spring tutorial and everything worked fine. After this, I decided to add delete and modify funcionalities. I've implemented them, but when I try to use them, I got the following error:
{"status":405,"error":"Method Not Allowed","message":"Request method 'POST' not supported","path":"/demo/delete"}
{"status":405,"error":"Method Not Allowed","message":"Request method 'POST' not supported","path":"/demo/modify"}
The commands that I'm executing:
curl localhost:8080/demo/delete -d name=First
curl localhost:8080/demo/modify -d name=First -d email=abc#gmail.com
//if the name doesn't exist in both methods, it will return "Nonexistent user!"
Below are the following code of:
MainController.java
package com.example.accessingdatamysql;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
#Controller
#RequestMapping(path="/demo")
public class MainController {
#Autowired
private UserRepository userRepository;
#PostMapping(path="/add")
public #ResponseBody String addNewUser (#RequestParam String name,
#RequestParam String email) {
User n = new User();
n.setName(name);
n.setEmail(email);
userRepository.save(n);
return "Saved\n";
}
#GetMapping(path="/all")
public #ResponseBody Iterable<User> getAllUsers() {
return userRepository.findAll();
}
/* ----------------- NEW METHODS THAT I'VE CREATED ----------------- */
#DeleteMapping(path="/delete")
public String delete(#RequestParam String name) throws Exception {
if(userRepository.findByName(name).equals(null)) {
System.out.println("Nonexistent user!\n");
}
userRepository.deleteByName(name);
return "User successfully deleted!\n";
}
#PutMapping(path="/modify")
public String modify(#RequestParam String name, #RequestParam String email) throws Exception {
if(userRepository.findByName(name).equals(null)) {
System.out.println("Nonexistent user!\n");
}
userRepository.deleteByName(name);
User n = new User();
n.setName(name);
n.setEmail(email);
userRepository.save(n);
return "User successfully modified!\n";
}
}
User.java
package com.example.accessingdatamysql;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
#Entity
public class User {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
private String name;
private String email;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
UserRepository.java
package com.example.accessingdatamysql;
import org.springframework.data.repository.CrudRepository;
import com.example.accessingdatamysql.User;
public interface UserRepository extends CrudRepository<User, Integer> {
public User findByName(String name);
public void deleteByName(String name);
}
I've no idea what's happening here. I've searched for other similar questions like that with the same error but none of them solved my problem.
You are executing the calls with the method POST rather than DELETE for /demo/delete and PUT for /demo/modify.
{"status":405,"error":"Method Not Allowed","message":"Request method 'POST' not supported","path":"/demo/delete"}
{"status":405,"error":"Method Not Allowed","message":"Request method 'POST' not supported","path":"/demo/modify"}
You are not showing how are you executing those failing calls, but for example, if you are using a GUI client such Postman to simulate the calls, check that you are selecting the proper HTTP method.
And if you are using the curl library in the terminal pay attention to the method set:
curl -X "DELETE" http://your.url/demo/delete ...
curl -X "PUT" http://your.url/demo/modify ...
After following #Dez's answer, the problem was solved but I was getting other error:
No EntityManager with actual transaction available for current thread - cannot reliably process 'persist' call
I solved it by adding #Transactional on DELETE and PUT methods in MainController class:
#DeleteMapping(path="/delete")
#Transactional //added
public void delete(#RequestParam String name) throws Exception { (...) }
#PutMapping(path="/modify")
#Transactional //added
public void modify(#RequestParam String name, #RequestParam String email) throws Exception { (...) }
And modified the exception throwing condition, that was always returning false:
if(userRepository.findByName(name).equals(null)) {
throw new Exception("Nonexistent user!");
}
to
if(userRepository.findByName(name) == null) {
throw new Exception("Nonexistent user!");
}
This is how #DeleteMapping and #PutMapping work. These annotations are shortcuts to #RequestMapping with method attribute fixed to DELETE or PUT correspondingly. To allow multiple HTTP methods for an endpoint you should list them explicitly:
#RequestMapping(path = "/modify", method = { RequestMethod.DELETE, RequestMethod.POST })
public String modify(...){
...
}

How to get rid of automatic installation of a new table

Problem: a new table is created once when I make a post request through The bash console. The rest of the queries go to the new table.
Than he does not like those databases which are available. As I understand - they just don't know, but I don't know how to direct it in the right. Although all variables are also named.
A problem was found created due to an Entity annotation in the Message class. Please tell me how to make it added to an existing table, tried #Table(name = "ApiTable") to an existing one, and it generates a new api_table.. Also don't quite understand what needs to be added/changed to accept json post requests.
Application
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
#SpringBootApplication
#EnableJpaRepositories("com.example.api")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
MainController
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
#Controller
#RequestMapping(path="/demo") /
public class MainController {
#Autowired
private UserRepository TestApi;
#PostMapping(path="/add")
public #ResponseBody String addNewUser (#RequestParam String name
, #RequestParam String email) {
Message n = new Message();
n.setName(name);
n.setEmail(email);
TestApi.save(n);
return "Saved";
}
#GetMapping(path="/all")
public #ResponseBody Iterable<Message> getAllUsers() {
return TestApi.findAll();
}
}
Message
import javax.persistence.*;
#Entity
public class Message {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
private String name;
private String email;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
UserRepository
import org.springframework.data.repository.CrudRepository;
public interface UserRepository extends CrudRepository<Message, Integer> {
}
application.properties
spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:mysql://localhost/Test?useUnicode=true&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root
The problem seems to be Spring Boot's default naming strategy which you'd have to replace.
Spring Boot's default naming strategy now seems to include converting camelCase to snake_case so you need to choose a different one (or implement your own).
Here's some more info on the topic: Hibernate naming strategy changing table names

Dropwizard: How to add a custom validation for GET / PUT

I have a dropwizard service in which I am trying to implement request validation, and below is the code for same.
import com.google.common.collect.ImmutableMap;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import com.fasterxml.jackson.annotation.JsonProperty;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.Produces;
import javax.ws.rs.Consumes;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
#Path("/system-info")
#Produces(MediaType.APPLICATION_JSON)
public class SystemInfo {
#GET
#Path("/get")
public Response testValidation(#QueryParam("name") String name,
#QueryParam("phoneNo") Long phoneNo,
#QueryParam("email") String email,
#QueryParam("password") String password) {
if(email == null) {
return Response.ok(ImmutableMap.of("status", "email missing")).build();
}
//bunch of other validations
return Response.ok(ImmutableMap.of("status", "ok")).build();
}
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Path("/post")
public Response testPostValidation(final Person person) {
if(person.getEmail() == null) {
return Response.ok(ImmutableMap.of("status", "email missing")).build();
}
return Response.ok(ImmutableMap.of("status", "ok")).build();
}
}
class Person {
#JsonProperty
private String name;
#JsonProperty
private String email;
#JsonProperty
private long phoneNo;
#JsonProperty
private String password;
public String getEmail(){
return email;
}
public Person(){};
}
In both the GET and POST method I have the QueryParams and the Person object which I would like to be validated.
I have the validation logic inside the Resource class itself, I can create a separate class and replace the if statements with
//if(email == null) {
// return Response.ok(ImmutableMap.of("status", "email missing")).build();
//}
if(!CustomValidater.validate(email, name, phone, password)) {
return Response.ok(ImmutableMap.of("status", "data missing")).build();
}
and do the same for POST as well this way the validation logic is abstracted in different class.
Is this the correct way of doing the validation or should I create custom annotation that will do this validation?
You should create custom validator, or add constraints validation to model.
Person should have properties like:
#NotNull
private String name;
#NotEmpty
private String surname;
...
And should be validated as method parameter if needed:
#Valid Person person

Validation not performed in a Spring controller

I want to use annotations in classes. I use javax.validation.constrants.* for annotations.
public final class EmailCredential implements Serializable {
private static final long serialVersionUID = -1246534146345274432L;
#NotBlank(message = "Sender must not be empty.")
#Email
private final String sender;
#NotBlank(message = "Subject must not be empty.")
private final String subject;
/// getters setters
}
None of them are working as expected. Meaning that when a below API gets called, annotations should throw error if annotated field is invalid. It looks like there is no annotation to check fields. How properly can I use annotations in a normal class?
controller:
#PostMapping(value = "/email/credentials", consumes = MediaType.APPLICATION_JSON_VALUE)
public Map<String, Object> emailCredentials(#RequestBody EmailCredential emailCredential) {
return emailService.setCredentials(emailCredential);
}
In your case the validation has to be specified to be triggered.
So add the #Valid annotation on the parameter(s) that you want to validate such as :
import javax.validation.Valid;
// ...
#PostMapping(value = "/email/credentials", consumes = MediaType.APPLICATION_JSON_VALUE)
public Map<String, Object> emailCredentials(#RequestBody #Valid EmailCredential emailCredential) {
return emailService.setCredentials(emailCredential);
}
According to Spring Boot official documentation : Validating Form Input
You should indicate that your EmailCredential need to be validated using the annotation #Valid
Here's an example from documentation :
package hello;
import javax.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
#Controller
public class WebController implements WebMvcConfigurer {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/results").setViewName("results");
}
#GetMapping("/")
public String showForm(PersonForm personForm) {
return "form";
}
#PostMapping("/")
public String checkPersonInfo(#Valid PersonForm personForm, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "form";
}
return "redirect:/results";
}
}

Error creating bean with name 'mainController': Unsatisfied dependency expressed through field 'userRepository'

I am having this error for several weeks now, and I do not know how to fix it. Similar solutions on Stack Overflow do not suit to my project.
I am currently using a mysql database, but encounter this problem whenever trying to start the server: StackTrace
[ERROR] Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:1.5.6.RELEASE:run (default-cli) on project iPbackend: An exception occurred while running. null: InvocationTargetException: Error creating bean with name 'mainController': Unsatisfied dependency expressed through field 'userRepository'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.resource.iPbackend.UserRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)
I am using this mainController:
MainController.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import com.resource.iPbackend.UserRepository;
import com.resource.iPbackend.User;
#Controller
#RequestMapping(path = "/main")
public class MainController {
#Autowired
private UserRepository userRepository;
#RequestMapping(path = "/reg", method = RequestMethod.POST)
public #ResponseBody String regNewUser (#RequestParam String firstName, #RequestParam String lastName, #RequestParam String email, #RequestParam String password, #RequestParam String username) {
User n = new User();
n.setFirstName(firstName);
n.setLastName(lastName);
n.setEmail(email);
n.setPassword(password);
n.setUsername(username);
userRepository.save(n);
return "User is stored in database: " + n;
}
#GetMapping(path = "/all")
public #ResponseBody Iterable<User> getAllUsers() {
return userRepository.findAll();
}
}
Together with this repository:
UserRepository.java
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import com.resource.iPbackend.User;
#Repository
public interface UserRepository extends CrudRepository<User, Long> {
}
And this Entity:
User.java
import org.springframework.data.annotation.Id;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String firstName;
private String lastName;
private String email;
private String password;
private String username;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
You need to include the annotation #EnableJpaRepositories in your base configuration class in order for Spring Data to register interfaces extending CrudRepository as spring beans.
Also the annotation #Repository does nothing on interfaces so this is not needed but it won't break anything by being there either.
EDIT:
You can also try and be more specific by telling the #EnableJpaRepositories annotation exactly where your repositories are. i.e.:
#EnableJpaRepositories("com.resource.iPbackened")
If you are using Springboot (#SpringBootApplication) then:
Try moving all your component classes in the same(or child) packages relative to your base Springboot config class.
For Instance:
I configured my SpringBoot base config class in package com.application
;
And I moved UserRepository(you mentioned) in package com.application.resource.iPbackend.User it worked for me without changing anything on the Annotation side. I got no error.
Since you havent shared the your Springboot Configuration so I am assuming you will be using defaults. If solution doesnt work, pl share your spring boot config which I can try on my side and respond.
Hope this helps!

Categories

Resources