@Valid@Validated差異


@Valid@Validated差異
@Valid@ValidはJSR-303規格です.org.hibernate.validator.internal.constraintvalidators体験値は、複数のValidatorによって実現される.
そのコアはLocalValidatorFactoryBeanで、スプリングを案内します.
使用しているので自動構成.
動作原理
デフォルトでは、コントローラに@Validがない場合でも
処理の有効性が検証された論理.
理由は?InvocableHandlerMethodは、適切なパラメータプロセッサを見つけようとした.HandlerMethodArgumentResolverCompositeに送信されます.

処理する解析器、getArgumentResolver()を探しています.private final Map<MethodParameter, HandlerMethodArgumentResolver>argumentResolverCache = new ConcurrentHashMap<>(256);この因子に含まれるRequestResponseBodyMethodProcessorによって

を使用して確認できます.RequestResponseBodyMethodProcessor彼はAbstractMessageConverterMethodArgumentResolverを引き継いだ.
この継承クラスのvalidateIfApplicableでは、ループを中心に操作が実行されます.
@Validがあるかどうかを検索します.
ある場合は、DataBinderオブジェクトに移動して検証します.
認証エラーが発生すると、MethodArgumentNotValidExceptionになります.
これはスプリングExceptionResolverDefaultHandlerExceptionResolverのおかげです.

400個のエラーが吐き出されます.
@Validated
@Validated(グローバルコントローラに貼り付け)
上記の@Validとは異なり、cglibはAOPベースのメソッドリクエストであるMethodValidationInterceptorは受け入れて処理する.
SampleControllerは普通類だからです.
インタフェースのように、JDKダイナミックエージェントではなくCglib proxyを使用します.

その後、ラインはプロキシリクエストの有効性検証をブロックします.Set<ConstraintViolation<Object>>result;空いていなければConstraintViolationExceptionを投げます.

エラーメッセージのデフォルト値はjavax.validation.constraints.XXX.message propertiesで定義されます.
これはDefaultHandlerExceptionResolverに登録されているオブジェクトではありません.
500エラーと一緒に外に吐きます.また、ExceptionHandlerを実現する必要がある.
次に、私が実装したソースコードの例を示します.
SampleController.java
import javax.validation.Valid;
import javax.validation.constraints.Min;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RestController
@Validated
public class SampleController {

    @PostMapping("/hello")
    public String hello(@Valid @RequestBody MessageRequest messageRequest) {
        log.info(messageRequest.getMessage());
        return "hello";
    }

    @GetMapping("/hi")
    public String hi(@Min(value = 1) int value) {
        log.info(String.valueOf(value));
        return "hi";
    }

}
MessageRequest.java
import javax.validation.constraints.NotNull;
import lombok.Getter;

@Getter
public class MessageRequest {

    @NotNull(message = "message는 null일 수 없습니다.")
    private String message;

}
SampleControllerTest.java
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.github.lsj8367.web.request.MessageRequest;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

@WebMvcTest(SampleController.class)
class SampleControllerTest {

    private MockMvc mockMvc;

    @Autowired
    private WebApplicationContext ctx;

    @Autowired
    private ObjectMapper objectMapper;

    @BeforeEach
    void setUp() {
        mockMvc = MockMvcBuilders.webAppContextSetup(ctx)
            .alwaysDo(print())
            .build();
    }

    @Test
    @DisplayName("Post @Valid 테스트")
    void test() throws Exception {
        final String obj = objectMapper.writeValueAsString(new MessageRequest());

        mockMvc.perform(post("/hello")
                .content(obj)
                .contentType(MediaType.APPLICATION_JSON_VALUE))
            .andExpect(status().isBadRequest());
    }

    @Test
    @DisplayName("Get @Validated 테스트")
    void hiTest() throws Exception {
        mockMvc.perform(get("/hi")
                .param("value", "0")
                .contentType(MediaType.APPLICATION_JSON_VALUE)
            )
            .andExpect(status().isInternalServerError());
    }