Hibernate #Validator with Spring Boot - java

I have a controller method that handles the POST request for a reset password form. The form has a hidden input field for the reset token and prompts the user for a new password.
I'd like to use the #Valid annotation in my code below, but not sure if I can use my existing User class.
Does Hibernate Validator require me to create an additional class specifically for the form?
Controller method
#RequestMapping(value = "/resetPassword", method = RequestMethod.POST)
public ModelAndView setNewPassword(#RequestParam Map<String,String> requestParams) {
User user = userService.findUserByResetToken(requestParams.get("token"));
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("message", "Form data is " + requestParams.get("token") + requestParams.get("password") + user.getEmail());
modelAndView.setViewName("resetPassword");
return modelAndView;
}
User class
#Entity
#Table(name = "user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private int id;
#Column(name = "email")
#Email(message = "Please provide a valid e-mail")
#NotEmpty(message = "Please provide an e-mail")
private String email;
#Column(name = "password")
#Size(min = 8, max = 72, message = "Your password must be between 8 and 72 characters long")
#NotEmpty(message = "Please provide a password")
#Transient
private String password;
#Column(name = "first_name")
#NotEmpty(message = "Please provide your first name")
private String firstName;
#Column(name = "last_name")
#NotEmpty(message = "Please provide your last name")
private String lastName;
#Column(name = "enabled")
private boolean enabled;
#Column(name = "confirmation_token")
private String confirmationToken;
#Column(name = "created_on")
private Date createdOn;
#Column(name = "last_login")
private Date lastLogin;
#Column(name = "reset_token")
private String resetToken;
// Getters and setters omitted
}

You ought to create a separate class, also better to use that class directly as your request body to be able to apply #Valid on it;
Controller Method
#RequestMapping(value = "/resetPassword", method = RequestMethod.POST)
public ModelAndView setNewPassword(#RequestBody #Valid PasswordUpdateRq passwordUpdateRq) {
User user = userService.findUserByResetToken(passwordUpdateRq.getToken());
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("message", "Form data is " + passwordUpdateRq.getToken() + passwordUpdateRq.getPassword() + user.getEmail());
modelAndView.setViewName("resetPassword");
return modelAndView;
}
New Request Bean
public class PasswordUpdateRq {
#Size(min = 8, max = 72, message = "Your password must be between 8 and 72 characters long")
#NotEmpty(message = "Please provide a password")
private String password;
private String token;
// Getters and setters omitted
}
This will make validation automatic as you've requested. The reason User class is not usable here is that there are other fields with #NotEmpty etc restrictions, which does not exist in your current form, and these would always fail the validation.
Extra comment: I think using the same object for controller layer validation & operations with database creates a high coupling between two, I'd recommend keeping table related classes under logic classes, and use interface beans that are endpoint specific in controller layer.

Related

At what point does validation become redundant in Spring?

So I have been playing around with Spring building a full stack application and I am at the point where I am validating data. First off, I want to validate that the incoming post request to my Spring Controller is able to create a User object and add it to the database. I created a custom validator using the combinator pattern and Java 8 features to do this.
Question 1 is there a way to intercept one of the JSON fields in post method? It is coming back as a String but I need a localdate to satisfy the user object creation/validation.
Question 2 when is it preferred to use validation annotations in the POJO object vs validating when the request comes through the controller? Should you be using both? Are there preferred patterns for validation? Obviosuly the client side will be validated as well before the info reaches the server.
//Using my custom validation here
#PostMapping
public ResponseEntity addUser(#RequestBody User user) {
UserValidation.ValidationResult result = userValidationService.validate(user);
if (result.equals(UserValidation.ValidationResult.SUCCESS)){
logger.info("Added a new user.");
userService.addUser(user);
return ResponseEntity.ok(HttpStatus.OK);
}
logger.info("Could not add new user.");
return new ResponseEntity(HttpStatus.BAD_REQUEST);
}
//Using annotation validation here on the POJO
#Data
#Table
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
#Length(max = 6)
private String firstName;
#NotNull
private String lastName;
private String username;
#Email
private String email;
private LocalDate birthDate;
private String password;
public User() {
}
public User(String firstName, String lastName, String username, String email, String password,
LocalDate birthDate) {
this.firstName = firstName;
this.lastName = lastName;
this.username = username;
this.email = email;
this.password = password;
this.birthDate = birthDate;
}
}
You can create a DTO object for Entity and do all validation there.
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
public class UserDto {
#NotNull
#Length(max = 6)
private String firstName;
#NotNull
private String lastName;
private String username;
#Email
private String email;
private LocalDate birthDate;
private String password;
}
Then put this object in Controller:
#PostMapping
public ResponseEntity addUser(#Valid #RequestBody UserDto userDto) { // #Valid will check if validation is ok in object UserDto
UserValidation.ValidationResult result = userValidationService.validate(userDto);
if (result.equals(UserValidation.ValidationResult.SUCCESS)){
logger.info("Added a new user.");
userService.addUser(toEntity(userDto)); //maps UserDto to User Entity
return ResponseEntity.ok(HttpStatus.OK);
}
logger.info("Could not add new user.");
return new ResponseEntity(HttpStatus.BAD_REQUEST);
}
// maps UserDto to User so you can save it to database in Service layer
User toEntity(UserDto userDto) {
User user = new User();
user.setFirstName(userDto.getFirstName);
user.setLastName(userDto.getUserlastName);
user.setUsername(userDto.getUsername);
user.setEmail(userDto.getEmail);
user.setPassword(userDto.getPassword);
user.setBirthDate(userDto.birthDate)
return user;
}

Get logged in user ID springboot and thymeleaf

I'm using Springboot and I want to create a profile.html page where the User can visualise and edit its informations.
Now, there are 2 tables on my db :
Credentials (login phase)
User (provides user informations
Credentials class :
#Entity
public class Credentials {
public static final String DEFAULT_ROLE = "DEFAULT";
public static final String ADMIN_ROLE = "ADMIN";
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(nullable = false, unique = true)
#NotEmpty
// #Size(min = 5, max = 250)
private String username;
#Column(nullable = false)
#NotEmpty
// #Size(min = 8, max = 20)
private String password;
#Column(nullable = false)
private String role = "DEFAULT";
#OneToOne(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
private User user;
public Credentials() {
}
public Credentials(String email, String newPassword,long newId) {
this.username=email;
this.password=newPassword;
this.id=newId;
this.user= new User();
}
//some getters and setters
}
And User class :
#Getter
#Setter
#Entity
#Table(name = "users")
public class User {
public User() {}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column
private String name ="";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
In profile page I want to show all User informations (in this case just its name) so I was thinking to get the current user inside ProfileController, add it to Model class and show all informations needed using html.
The problem is that I don't know how to get the current user.
I think the best way to do this is through Id inside Credentials class because it is the foreign key of User table but I don't know how to get this id. Maybe Springboot Authentication class can help?
I was thinking to retrieve, don't know how, current Credentials class with Authentication and use its id to get the linked User (fetchType.EAGER).
This is pseudocode for ProfileController page :
#Controller
public class ProfileController {
#Autowired
UserService us;
#RequestMapping(value="/profile",method = RequestMethod.GET)
public String showProfilePage(Model model) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String currentUserName = authentication.getName();
Credentials c = getCredentialsFromService() //How I can do this?Is it the right way?
User currentUser = c.getUser();
model.addAttribute("user", currentUser ) ;
return "profile";
}
}

how to do validation on only changed field in update api?

how to do validation on only changed field which come as json on update api.
project use:
java+maven+hibernate+spring mvc
#Entity
#Component("user")
#Table(name="user")
#DynamicUpdate
#Inheritance(strategy = InheritanceType.JOINED)
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Range(min = 0, max = 1000000)
#Column(name="user_id")
private int UserId;
#Size(min = 6, max = 20)
#Column(name="password")
//#org.hibernate.annotations.Type(type="StringType")
private String password;
#Size(min=2, max=20)
#Column(name="first_name")
private String firstName;
#Size(min=2, max=20)
#Column(name="last_name")
private String lastName;
#Size(min=2,max=20)
#Column(name="surname")
private String surName;
#Email
#Column(name="email_address")
private String EmailAddress;
#Size(min = 10, max = 10)
#Phone(message = "please enter valid phone number")
#Column(name="mobile_number")
private String mobileNumber;
}
and my update api as bellow:
#SuppressWarnings({ "unchecked", "rawtypes" })
#PostMapping(value = "/candidate")
public ResponseEntity addCandidate(#Valid #RequestBody Candidate candidate, BindingResult result) {
Map<String, Object> errors = new HashMap<String, Object>();
if (result.hasErrors()) {
here save error in errors list.
}
errors.put("error", errorList);
return new ResponseEntity(errors, HttpStatus.NOT_ACCEPTABLE);
}
candidateService.addCandidate(candidate);
return new ResponseEntity(HttpStatus.CREATED);
}
due to the above logic, it will check all attributes which do not update actually.
what are other methods to apply validation only on changed attributes?
more explanation:
I have three class
user
candidate
employee
here user class contains all common information of candidate and employee and in a forther password also contains for login.
now when first time signs up, I want to do validation only on email + password only, but here due to #valid annotation, it does validation on all the attribute.

Spring boot REST api ConstraintViolationException on password field

I'm having a weird issue on my Spring backend, where I am sending a post request with a domain User object from my angular2 frontend that is being recieved by a REST API endpoint and translating it into the Spring User model object. The issue is that the password field from the JSON request seems to not be translating to the password field on the backend. All the other fields (username, name, email, etc.) are coming through fine, but the password is null.
Request payload as seen from network tab in chrome
email : "asdf#asdf.com" firstName : "asdfasdjk" lastName : "asdfs"
login : "adfsjk" password : "fasdfasdfasdsd"
Response seen from network tab
error: "Internal Server Error" exception: "javax.validation.ConstraintViolationException" message: "Validation failed for classes [domain.User] during persist time for groups [javax.validation.groups.Default, ]
List of constraint violations:[ ConstraintViolationImpl{interpolatedMessage='may not be null', propertyPath=password, rootBeanClass=class domain.User, messageTemplate='{javax.validation.constraints.NotNull.message}'}]"
path:"/api/users" status: 500
Spring Rest method:
#RequestMapping(value = "/users",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
#Timed
public ResponseEntity<User> createUser(#RequestBody User user) throws URISyntaxException {
User result = userRepository.save(user);
return ResponseEntity.created(new URI("/api/users/" + result.getId()))
.headers(HeaderUtil.createEntityCreationAlert("user", result.getId().toString()))
.body(result);
}
Spring domain object
#Entity
#Table(name = "user")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class User extends AbstractAuditingEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotNull
#Size(max = 100)
#Column(length = 100, unique = true)
private String login;
#JsonIgnore
#NotNull
#Size(min = 5, max = 60)
#Column(length = 60)
private String password;
#Size(max = 50)
#Column(name = "first_name", length = 50)
private String firstName;
#Size(max = 50)
#Column(name = "last_name", length = 50)
private String lastName;
#Email
#Size(max = 100)
#Column(length = 100, unique = true)
private String email;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "user_tag",
joinColumns = #JoinColumn(name = "users_id", referencedColumnName = "ID"),
inverseJoinColumns = #JoinColumn(name = "tags_id", referencedColumnName = "ID"))
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
private List<Tag> tags = new ArrayList<>();
}
Angular2 domain object
export class User {
id: number;
login: string;
password: string;
firstName: string;
lastName: string;
email: string;
}
The problem is with using #JsonIgnore on the password field, this makes the filed ignorable when reading from or writing to JSON message, one solution is to use #JsonProperty with access = Access.WRITE_ONLY as follows
public class User extends AbstractAuditingEntity implements Serializable {
// Other fileds
#NotNull
#JsonProperty(access = Access.WRITE_ONLY)
#Size(min = 5, max = 60)
#Column(length = 60)
private String password;
}
Another solution is to implement a getter and setter for the password field, and annotate the getter with #JsonIgnore and the setter with #JsonProperty, as follows
public class User extends AbstractAuditingEntity implements Serializable {
// Other fileds
#NotNull
#Size(min = 5, max = 60)
#Column(length = 60)
private String password;
#JsonIgnore
public String getPassword() {
return password;
}
#JsonProperty
public void setPassword(String password) {
this.password = password;
}
}

Want to display enum name corresponding to value in jsp

I am new to Spring and trying to populate user details in a table, the table will show some basic information of Users of system
Structure of table is
Firstname Lastname Email Phone Number Role State
I am using Spring 4 and Hibernate 4.
My Model class
#Entity
#Table(name = "USERS")
public class User {
#Id
#Column(name = "USER_ID")
private String userId;
#Column(name = "PASSWORD")
private String password;
#Column(name = "FIRST_NAME")
private String firstName;
#Column(name = "MIDDLE_NAME")
private String middleName;
#Column(name = "LAST_NAME")
private String lastName;
#Column(name = "EMAIL")
private String email;
#Column(name = "PHONE")
private String phone;
#Column(name = "STATE")
private int state=State.ACTIVE.getState();
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "USER_ROLE_MAP", joinColumns = { #JoinColumn(name = "USER_ID") }, inverseJoinColumns = {
#JoinColumn(name = "ROLE_ID") })
private Set<Role> blogRoles = new HashSet<>();
/*getters and setter and other utility methods are remove*/
My Controller method of user list
#RequestMapping(value = "/listUser", method = RequestMethod.GET)
public ModelAndView showAllUsers() {
System.out.println("start login");
ModelAndView modelAndView=new ModelAndView("listUser");
List<User> userList=userService.findAll();
System.out.println("user List controler "+userList);
modelAndView.addObject("currentUser", getPrincipal());
modelAndView.addObject("userList", userList);
return modelAndView;
}
My Jsp code to display the user list data
<c:forEach items="${userList}" var="user">
<tr>
<td>${user.firstName}</td>
<td>${user.lastName}</td>
<td>${user.email}</td>
<td>${user.phone}</td>
<td>
<c:forEach items="${user.blogRoles}" var="role">
${role.roleType}
</c:forEach>
</td>
<td>${user.state}</td>
</tr>
</c:forEach>
Now I have a enum which has information about the state of user
public enum State {
ACTIVE(1), INACTIVE(2), LOCKED(3), DELETED(4);
private int state;
private State(int state) {
this.state = state;
}
public int getState() {
return state;
}
#Override
public String toString(){
return this.name();
}
public String getName(){
return this.name();
}
}
In JSP the state comes as integer
I want to display the name, the string value corresponding to the state which is an integer injected by spring and fetched by hibernate from DB.
I know there are some ways to do it like I can create a map from enum and send it's object to JSP or defining my own custom TLD.
But I am still not satisfied with these kind solutions, is there any better way to display the value with some more standard way or using some features of spring and EL ?
Any suggestion and help will be help full, thanks.
Here is one way to make it work. In your User class, add #Transient instance variable called stateName. #Transient means that Hibernate/JPA will NOT try to persist this field in the database! You would only use it for your UI/JSP purposes.
#Entity
#Table(name = "USERS")
public class User {
#Column(name = "STATE")
private int state=State.ACTIVE.getState();
#Transient
private String stateName;
So, when a User object gets populated from the DB, you would want to populate the stateName field with the correct STATE from your enum.
Something like:
user.setStateName(/*get the STATE by the INTEGER id*/)
Then, you can render it in the the JSP page like:
<td>${user.state}</td>
<td>${user.stateName}</td>
Hope this help!
RECOMMENDATION
Never send your Domain model to the UI, always use a Proxy/Transfer object for UI purposes.

Categories