18 Eylül 2023 Pazartesi

SpringMVC MethodArgumentNotValidException Sınıfı

Giriş
Şu satırı dahil ederiz
import  org.springframework.web.bind.MethodArgumentNotValidException;
Exception döndürmek için 3 yöntem var
- @ControllerAdvice + @ExceptionHandler : Global Exception handling yapar
- HandlerExceptionResolver
- ResponseStatusException
Kullanım
MethodArgumentNotValidException sınıfının 
1. getBindingResult() metodu çağrılır ve bir BindingResult nesnesi elde edilir. 
2. Bu nesnenin getAllErrors() metodu kullanılarak ObjectError nesneleri dolaşılır. 
3. ObjectError nesnesinin getDefaultMessage() metodu ile hata mesajı alınır. getRejectedValue() value ile kabul edilmeyen değer alınabilir.

@ControllerAdvice
Örnek
Şöyle yaparız.
@ControllerAdvice
public class BillingExceptionHandler extends ResponseEntityExceptionHandler {

@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(
MethodArgumentNotValidException arguments,
  HttpHeaders headers,
  HttpStatus status,
  WebRequest request) {

  BindingResult bindingResult = arguments.getBindingResult();
  List<String> validationMessages = new ArrayList<>();
  List<ObjectError> objectErrors = bindingResult.getAllErrors();
  for (ObjectError objectError : objectErrors) {
    String defaultMessage = objectError.getDefaultMessage();
    validationMessages.add(defaultMessage);
  }
  return new ResponseEntity<>(validationMessages, HttpStatus.BAD_REQUEST);
}
}
Örnek - Map<String,Object> 
Şöyle yaparız
@ControllerAdvice
public class GlobalExceptionHandler {

  @ExceptionHandler(value = MethodArgumentNotValidException.class) // 1.
  public ResponseEntity<Map<String,Object>>handleException(
    HttpServletRequest request, MethodArgumentNotValidException e) {

    var fieldErrors = e.getFieldErrors();

    List<Map<String,String>> fieldErrorsMap = fieldErrors.stream() // 2.
      .map(fieldError -> {
        var fieldErrorObject = new Object() {
        final String fieldName = fieldError.getField();
        final String errorMessage = fieldError.getDefaultMessage();
      };
      return Map.ofEntries(
        Map.entry("field", fieldErrorObject.fieldName),
        Map.entry("message", fieldErrorObject.errorMessage != null
                                    ? fieldErrorObject.errorMessage
                                    : ""));})
      .collect(Collectors.toList());

      Map<String,Object> errorResponsePayload =
        Map.of("path",request.getServletPath(), "errors",fieldErrorsMap); // 3.

      return ResponseEntity.badRequest().body(errorResponsePayload);
  }
}
@RestControllerAdvice
Aslında @ControllerAdvice ile aynı şey.
Örnek - Map<String, String>
Şöyle yaparız.
@RestControllerAdvice
public class CustomExceptionHandler {

  @ExceptionHandler(MethodArgumentNotValidException.class)
  public ResponseEntity<Object> handleValidationExceptions(
    MethodArgumentNotValidException ex) {
    Map<String, String> errors = new HashMap<>();
    ex.getBindingResult().getAllErrors().forEach((error) -> {
      String fieldName = ((FieldError) error).getField();
      String errorMessage = error.getDefaultMessage();
      errors.put(fieldName, errorMessage);
    });
    return ResponseEntity.badRequest().body(errors);
  }
}
@ExceptionHandler
Açıklaması şöyle
1. @ExceptionHandler Instructs Spring which exception type should handle.
2. We’re extracting all the invalid field errors, they contain the validation metadata for each validation that failed for a specific bean attribute.
3. Creating a map that will be returned as a JSON format containing the endpoint path and an array with the field errors.
Örnek - Map<String, String>
ResponseEntityExceptionHandler sınıfını kullanmak zorunda değiliz. Sadece bir RestController içinde özel bir şey yapmak istersek RestController sınıfına şöyle bir metod ekleriz. MethodArgumentNotValidException sınıfının getBindingResult() metodu çağrılır ve bir BindingResult nesnesi elde edilir. Bu nesnenin getAllErrors() metodu kullanılarak ObjectError nesneleri dolaşılır. Ancak bu sefer ObjectError nesnesini FieldError tipine cast ediyoruz. Böylece FieldError nesnesinin getField() metodunu kullanarak hatalı alanın ismine de erişebiliriz.
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
public Map<String, String> handleValidationExceptions(MethodArgumentNotValidException ex) {

  Map<String, String> errors = new HashMap<>();
  ex.getBindingResult().getAllErrors().forEach((error) -> {
    String fieldName = ((FieldError) error).getField();
    String errorMessage = error.getDefaultMessage();
    errors.put(fieldName, errorMessage);
  });
  return errors;
}

Örnek
Şöyle yaparız. Bu örnek diğerlerinden farklı olarak hangi değerin kabul edilmediğini de gösterir
public class ApiValidationError {
    private String field;
    private String message;
    private Object rejectedValue;

    // Constructors, Getters, Setters
}

@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<List<ApiValidationError>> handleDetailedValidationExceptions(
  MethodArgumentNotValidException ex) {
  List<ApiValidationError> errors = ex.getBindingResult()
    .getFieldErrors()
    .stream()
    .map(err -> new ApiValidationError(err.getField(), err.getDefaultMessage(),
      err.getRejectedValue()))
    .collect(Collectors.toList());
    
  return ResponseEntity.badRequest().body(errors);
}

Hiç yorum yok:

Yorum Gönder