SpringBoot의 Validation을 사용 해 보자 Using Validation in SpringBoot
What is Spring Validation?
Validation is one of the most essential parts of programming.
In Java/Kotlin, when you try to access a null value, a null pointer exception occurs. The process of verifying in advance to prevent such issues is called Validation.
Validation
- Code becomes complex when there are many values to validate
- Validation should have high reusability and needs to be separated from Service Logic
- When Logic needs to change, having Validation mixed in makes it very messy
Annotations for Validation
| Size | String length | Future | Future date |
|---|---|---|---|
| NotNull | Not null | FutureOrPresent | Today or future |
| NotEmpty | Not null, ‘’ | Pattern | Regex |
| NotBlank | Not null,’’, ‘ ‘ | Max | Maximum value |
| Past | Past date | Min | Minimum value |
| PastOrPresent | Today or past | Valid | Object validation |
Let’s think about where to apply validation. I’m going to simply apply it to a project I’m starting. I want to write the part that checks when user information is received during registration.
DTO to Receive Data
-
getter, setter, and toString are omitted as they’re too long
package com.wool.modulink.dto.user; public class User { private String name; private String password; private String email; private String phone; private int age; // getter, setter, toString }
Controller
package com.wool.modulink.controller.user;
import com.wool.modulink.dto.user.User;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/auth")
public class AuthController {
@PostMapping("/user")
public User user(@RequestBody User user) {
System.out.println(user);
return user;
}
}
POST to localhost:8080/auth/user
{
"name":"test",
"password":"1234",
"email":"paullee@mail.com",
"phone":"01012341234",
"age":1000
}
- The data above looks fine at first glance.
- For email and phone, formats may differ depending on users
- For age… I haven’t seen anyone who has lived about 1000 years…! We should put input restrictions on age too
- In line with the purpose of this post, let’s use Spring Validation instead of Service Logic
Applying Validation
Traditional Method
Previously, we used if statements to check inside the controller or service logic like this:
package com.wool.modulink.controller.user;
import com.wool.modulink.dto.user.User;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/auth")
public class AuthController {
@PostMapping("/user")
public ResponseEntity user(@RequestBody User user) {
System.out.println(user);
if(user.getAge() > 200){
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(user);
}
if(user.getEmail().contains("@")){
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(user);
}
return ResponseEntity.ok(user);
}
}
Now I’m going to change the above to something prettier and simpler using methods provided by Spring.
For the default Validations to be applied, annotations must be applied to the data where you want to validate.
Let’s first add Valid to the basic controller and move on.
Email Validation
For email, use the @Email annotation on the variable that holds the email in the DTO.
And for the annotation to work, you need to tell Spring “I will validate” with the @Valid annotation in front of the controller’s RequestBody.
UserDto
import javax.validation.constraints.Email;
public class User {
private String name;
private String password;
@Email
private String email;
private String phone;
private int age;
}
UserController
package com.wool.modulink.controller.user;
import com.wool.modulink.dto.user.User;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
@RestController
@RequestMapping("/auth")
public class AuthController {
@PostMapping("/user")
public ResponseEntity user(@Valid @RequestBody User user) {
System.out.println(user);
}
}
Phone Validation
This part can be applied anywhere using regex with the regexp option of the @Pattern annotation
UserDto
package com.wool.modulink.dto.user;
import javax.validation.constraints.Email;
import javax.validation.constraints.Pattern;
public class User {
private String name;
private String password;
@Email
private String email;
@Pattern(regexp = "^\\d{2,3}-\\d{3,4}-\\d{4}$",message = "Does not match phone number format. 01x-xxx(x)-xxxx")
private String phone;
private int age;
}
Setting Max/Min Values
When numbers come in, you can limit age to 1 or more and 100 or less.
You need to set max/min values according to each situation.
UserDto
public class User {
private String name;
private String password;
@Email
private String email;
@Pattern(regexp = "^\\d{2,3}-\\d{3,4}-\\d{4}$",message = "Does not match phone number format. 01x-xxx(x)-xxxx")
private String phone;
@Min(value = 0,message = "Please enter an appropriate age.")
@Max(value = 150,message = "Please enter an appropriate age.")
private int age;
}
Collecting Validation Errors
There’s something that collects all validations at once. It’s called BindingResult, which holds all values that failed validation. You can extract error messages through a loop.
public ResponseEntity user(@Valid @RequestBody User user, BindingResult bindingResult) {
if(bindingResult.hasErrors()) {
StringBuilder sb = new StringBuilder();
bindingResult.getAllErrors().forEach(objectError -> {
FieldError field = (FieldError) objectError;
String message = objectError.getDefaultMessage();
System.out.println(field.getField() + ": " + message);
sb.append("field: " + field.getField());
sb.append("message: " + message);
});
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(sb.toString());
}
}
Let’s Test
{
"name":"test",
"password":"1234",
"email":"paulleeemail.com",
"phone":"01012341234",
"age":1000
}
When sending the above data, the console shows:
phone: Does not match phone number format. 01x-xxx(x)-xxxx
age: Please enter an appropriate age.
email: Must be a properly formatted email address
It would be good to work on formatting the API return value nicely later.
댓글남기기