スプリングの各種機能(Vailation、Exception処理)
18602 ワード
スプリングの機能を利用する。
😡😎
Spring Boot Validation
検証は非常に重要な部分です。JavaがNull値にアクセスしようとするとNullポインタ異常が発生します。これを回避するために、事前検証プロセスはValidationと呼ばれます。
前述したように,簡単に検証するだけでよいが,オブジェクトが多い場合は大量のコードを記述する必要がある.正常な論理が必要ですが、ビジネスに関係のないコードがたくさん必要です.整理すると.
そのため、彼らはベースとしてサポートを提供します。
やってみよう。
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-validation'
}
😎Dependancyの追加public class User {
private String name;
private int age;
@Email
private String email;
@Pattern(regexp = "^\\d{2,3}-\\d{3,4}-\\d{4}$")
private String phoneNumber;
...
...
}
😎User Classのメンバーに電話します. @PostMapping("/user")
public User user(@Valid @RequestBody User user){
System.out.println("user : "+user);
return user;
}
😎@ValidをAPIのパラメータに貼り付けて終了😎上記のように、素材ごとに入れると成功しますが
😎上記のEメール形式に違反したり、正規表現のPattern(電話番号)に不満がある場合は、を選択します.
2022-01-19 18:16:55.333 WARN 7248 --- [nio-8080-exec-3] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public com.example.validation.dto.User com.example.validation.controller.ApiController.user(com.example.validation.dto.User) with 2 errors: [Field error in object 'user' on field 'phoneNumber': rejected value [0101234-1234]; codes [Pattern.user.phoneNumber,Pattern.phoneNumber,Pattern.java.lang.String,Pattern]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [user.phoneNumber,phoneNumber]; arguments []; default message [phoneNumber],[Ljavax.validation.constraints.Pattern$Flag;@715f31ba,^\d{2,3}-\d{3,4}-\d{4}$]; default message ["^\d{2,3}-\d{3,4}-\d{4}$"와 일치해야 합니다]] [Field error in object 'user' on field 'email': rejected value [asdf]; codes [Email.user.email,Email.email,Email.java.lang.String,Email]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [user.email,email]; arguments []; default message [email],[Ljavax.validation.constraints.Pattern$Flag;@41a14204,.*]; default message [올바른 형식의 이메일 주소여야 합니다]] ]
😡こんなに悲しくなったエラーメッセージの設定方法public User user(@Valid @RequestBody User user
, BindingResult bindingResult) { //BindingResult는
if(bindingResult.hasErrors()){ //에러가 있으면
StringBuilder sb = new StringBuilder();
bindingResult.getAllErrors().forEach(objectError -> {
FieldError fidld = (FieldError) objectError; //어떤필드 애러인가?
String message = objectError.getDefaultMessage(); //그 애러 매세지 뭔가?
System.out.println("field : " + fidld.getField());
System.out.println(message);// 메세지출력
});
}
System.out.println("user : " + user);
return user;
}
😡この時の情報は間違っていました...変な情報があります. @Pattern(regexp = "^\\d{2,3}-\\d{3,4}-\\d{4}$",message = "핸드폰 번호의 양식과 맞지 않습니다.. 01x-xxxx-xxxx")
private String phoneNumber;
😎 ああ...Custom Validation
@AssertTrue(message = "yyyyMM 의 형식에 맞지 않습니다.")
public boolean isReqYearMonthValidation() { //boolean메서드는 is를 붙여주자
System.out.println("그럼 여기는?");
//DateTimeFormatter는 yyyyMM"dd" 까지 붙이기 때문에 01일을 임의로 붙여줌
try{
//String "000000" 을 날짜로 파싱
LocalDate localDate = LocalDate.parse(getReqYearMonth() + "01", DateTimeFormatter.ofPattern("yyyyMMdd"));
}catch (Exception e){
return false;
}
return true;
}
これにより、Userクラスに配置できますが、再利用できません.再利用する場合は、1つずつコピーして入れる必要があります.コードが長くなり、重複性も増加します.だからこれを無装飾にしましょう@Constraint(validatedBy = {YearMonthValidator.class})// 이 클래스를 사용해서 검사할꺼다....
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface YearMonth {
String message() default "yyyyMM 형식에 맞지 않습니다.";
Class<?>[] groups() default { };
😎 Class<? extends Payload>[] payload() default { };
String pattern() default "yyyyMMdd";
}
😎委員会のために参考条件を制定する必要がある.public class YearMonthValidator implements ConstraintValidator<YearMonth, String> {
private String pattern;
@Override
public void initialize(YearMonth constraintAnnotation) {
this.pattern = constraintAnnotation.pattern();
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
// yyyyMM
try{
LocalDate localDate = LocalDate.parse(value+"01" , DateTimeFormatter.ofPattern(this.pattern));
}catch (Exception e){
return false;
}
return true;
}
}
😎 では上記の条件をチェックしたところに@annotationを入れて終了です~~// @Size(min = 6, max = 6)
@YearMonth
private String reqYearMonth; //yyyyMM
😎 では今yyymm形式をチェックするには@YaearMothを入れるだけです.Exception処理
😎 Webアプリケーションの観点から、エラーが発生した場合、エラーを低減できる方法は多くありません.
やってみよう。
@NotEmpty
@Size(min = 1,max = 10)
private String name;
@Min(1)
@NotNull
private Integer age;
😎 userdtoを作成し、Vald条件を掛けます. @GetMapping("")
public User get(@RequestParam(required = false) String name, @RequestParam(required = false) Integer age){
User user =new User();
user.setName(name);
user.setAge(age);
int a = 10+age;
return user;
}
😎 コントローラを作成します.何もしないでGetを呼び出すとNullPointexactionが表示されます
java.lang.NullPointerException: null
😎 基本的に、スプリングには、自身の例外に対して500個のエラーが簡単に送信されることがわかります.😎Postコールもそうです.条件に合わなければ400エラーしかあげられません.
どのように処理しますか?
ControllerAdviceとExceptionHandlerの使用
グローバルコントローラAdviceを作成します.
//@ControllerAdvice // ViewResolver 쓰면 이거 쓰면됨
@RestControllerAdvice //REST쓰면
public class GlobalControllerAdvice {
@ExceptionHandler(value = Exception.class)
public ResponseEntity exception(Exception e){
System.out.println("==============================");
System.out.println(e.getLocalizedMessage()); //에러 메세지 확인해보기
System.out.println("==============================");
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(""); // 서버에서 일어난 에러 500에러
}
}
もう一度お願いします.==============================
Validation failed for argument [0] in public com.ex......
==============================
😎このままコンソールウィンドウに現れたら、私が欲しい値をあげてもいいですか?きちんと😎しかし、このようにするとどのような間違いなのか分からないので、すべての間違いがこうなるので、もう少し設定しましょう.
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public ResponseEntity methodArgumentNotValidException(MethodArgumentNotValidException e){
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
}
ExceptionHandlerをもう1つ作成して例外を放出します.きれいじゃないけど、見える.🔔😁🎅🤔
🤔もう少し深化して、ExceptionとValidationを書き出します。
//@ControllerAdvice // ViewResolver 쓰면 이거 쓰면됨
@RestControllerAdvice(basePackageClasses = ApiController.class) //REST쓰면
public class GlobalControllerAdvice {
@ExceptionHandler(value = Exception.class)//모든 Exception
public ResponseEntity exception(Exception e){
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(""); // 서버에서 일어난 에러 500에러
}
@ExceptionHandler(value = MethodArgumentNotValidException.class)//
public ResponseEntity methodArgumentNotValidException(MethodArgumentNotValidException e){
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
}
}
🎅 (basePackageClasse=ApiController.class)この項目を追加すると、クラスで発生したことのみが処理されます.@Validated
public class ApiController {
@GetMapping("")
public User get(
@Size(min=2)
@RequestParam String name,
@NotNull
@Min(1)
@RequestParam Integer age){
User user =new User();
user.setName(name);
user.setAge(age);
return user;
}
🎅 GetMappingはdto経由で直接掛けなくても大丈夫ですクラスの上に@Validatedを置くのを忘れないでください🤔 ではGetは一般的にConstraintViolationExceptionエラーとMissingです...主に間違いがあるので、基本的に彼を捕まえなければならない.
@RestControllerAdvice(basePackageClasses = ApiController.class) //REST쓰면
public class GlobalControllerAdvice {
@ExceptionHandler(value = Exception.class) //모든 Exception
public ResponseEntity exception(Exception e){
System.out.println(e.getClass().getName() + " 예외가 나온 클래스 이름입니다.");
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(""); // 서버에서 일어난 에러 500에러
}
@ExceptionHandler(value = MethodArgumentNotValidException.class) // 잘못된 값을 Post받았을때 나오는 애러
public ResponseEntity methodArgumentNotValidException(MethodArgumentNotValidException e){
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
}
@ExceptionHandler(value = ConstraintViolationException.class) // 조건에 벗어나면 나오는 애러 (GET)
public ResponseEntity constraintViolationException(ConstraintViolationException e){
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
}
@ExceptionHandler(value = MissingServletRequestParameterException.class) //아무값도 입력안하면 나오는 애러 (GET)
public ResponseEntity missingServletRequestParameterException(MissingServletRequestParameterException e){
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
}
}
🎅 これで400個の楽子が出てくるでしょう?ちなみに、500の哀楽を絶対に出さないでください.つまり、すべてのテロリストは事前に予想して捕まえるべきだ.@ExceptionHandler(value = MethodArgumentNotValidException.class) // 잘못된 값을 Post받았을때 나오는 애러
public ResponseEntity methodArgumentNotValidException(MethodArgumentNotValidException e){
BindingResult bindingResult = e.getBindingResult(); //bindingResult는 애러 정보드를 담고있다.
bindingResult.getAllErrors().forEach(error -> { //안을 한번 보자.
FieldError fieldError = (FieldError) error; //에러를 형변환 시켜주고
String fieldName = fieldError.getField(); // 이름
String message = fieldError.getDefaultMessage(); // 에러 메세지
String value =fieldError.getRejectedValue().toString(); //어떤 값이 주입됬는지 보자
System.out.println("fieldName : " +fieldName);
System.out.println("message : " +message);
System.out.println("value : " +value);
System.out.println();
});
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
}
🎅 PostValidから情報の表示を開始します.上記のようにこれらの値を記入して送信します.fieldName : name
message : 비어 있을 수 없습니다
value :
fieldName : name
message : 크기가 1에서 10 사이여야 합니다
value :
fieldName : age
message : 1 이상이어야 합니다
value : 0
🎅 コンソールウィンドウにエラーメッセージが表示されます.このようにしてすべての人を捕まえる. @ExceptionHandler(value = MethodArgumentNotValidException.class) // 잘못된 값을 Post받았을때 나오는 애러
public ResponseEntity methodArgumentNotValidException(MethodArgumentNotValidException e){
BindingResult bindingResult = e.getBindingResult(); //bindingResult는 애러 정보드를 담고있다.
bindingResult.getAllErrors().forEach(error -> { //안을 한번 보자.
FieldError fieldError = (FieldError) error; //에러를 형변환 시켜주고
String fieldName = fieldError.getField(); // 이름
String message = fieldError.getDefaultMessage(); // 에러 메세지
String value =fieldError.getRejectedValue().toString(); //어떤 값이 주입됬는지 보자
System.out.println("fieldName : " +fieldName);
System.out.println("message : " +message);
System.out.println("value : " +value);
System.out.println();
});
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
}
@ExceptionHandler(value = ConstraintViolationException.class) // 조건에 벗어나면 나오는 애러 (GET)
public ResponseEntity constraintViolationException(ConstraintViolationException e){
e.getConstraintViolations().forEach(error ->{
Stream<Path.Node> stream = StreamSupport.stream(error.getPropertyPath().spliterator(), false);
List<Path.Node> list = stream.collect(Collectors.toList());
String field = list.get(list.size()-1).getName();
String message = error.getMessage();
String invalidValue = error.getInvalidValue().toString();
System.out.println("field : " +field);
System.out.println("message : " +message);
System.out.println("invalidValue : " +invalidValue);
System.out.println();
});
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
}
@ExceptionHandler(value = MissingServletRequestParameterException.class) //아무값도 입력안하면 나오는 애러 (GET)
public ResponseEntity missingServletRequestParameterException(MissingServletRequestParameterException e){
String fieldName = e.getParameterName();
String fieldType = e.getParameterType();
String invalidValue = e.getMessage();
System.out.println("fieldName : " + fieldName);
System.out.println("fieldType : " + fieldType);
System.out.println("invalidValue : " + invalidValue);
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
}
🎅 ここで重要なのは、間違った内容を見て、みんなに見せてあげることです!!!
デバッガを使用して表示
単純なシステム.out.クライアントがprintlnエラーの内容を見て理解できるようにすることが重要です!!!
もっときれいにしましょう。
public class ErrorResponse {
//클라이언트가 알아보기 편하게 해야함
private String statusCode;
private String requestUrl;
private String code;
private String message;
private String resultCode;
private List<Error> errorList;
...
...
}
🎅 エラーを含むErrorResponseクラスを作成します.public class Error {
private String field;
private String message;
private String invalidValue;
🎅 エラーの詳細を含むErrorクラスを作成します.
List<Error> errorList = new ArrayList<>();
BindingResult bindingResult = e.getBindingResult(); //bindingResult는 애러 정보드를 담고있다.
bindingResult.getAllErrors().forEach(error -> { //안을 한번 보자.
...
...
에러 정보꺼내오는 코드들...
...
Error errorMessage = new Error();
errorMessage.setField(fieldName);
errorMessage.setMessage(message);
errorMessage.setInvalidValue(invalidValue);
errorList.add(errorMessage);
});
ErrorResponse errorResponse = new ErrorResponse();
errorResponse.setErrorList(errorList);
errorResponse.setMessage("");
errorResponse.setRequestUrl(httpServletRequest.getRequestURI());
errorResponse.setStatusCode(HttpStatus.BAD_REQUEST.toString());
errorResponse.setResultCode("FAIL");
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse);
🎅 上記のように.WoWのようにきれいに整理された状態で、お客様にエラーメッセージを表示することができます!!
Reference
この問題について(スプリングの各種機能(Vailation、Exception処理)), 我々は、より多くの情報をここで見つけました https://velog.io/@stpn94/스프링의-다양한-기능テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol