3 분 소요


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.

댓글남기기