spring @Valid Validation 기본 오류 메시지를 커스터마이즈하려면 어떻게 해야 하나요?
DTO:
public class User {
@NotNull
private String name;
@NotNull
private String password;
//..
}
컨트롤러:
@RequestMapping(value = "/user", method = RequestMethod.POST)
public ResponseEntity<String> saveUser(@Valid @RequestBody User user) {
//..
return new ResponseEntity<>(HttpStatus.OK);
}
기본 json 오류:
{"timestamp":1417379464584,"status":400,"error":"Bad Request","exception":"org.springframework.web.bind.MethodArgumentNotValidException","message":"Validation failed for argument at index 0 in method: public org.springframework.http.ResponseEntity<demo.User> demo.UserController.saveUser(demo.User), with 2 error(s): [Field error in object 'user' on field 'name': rejected value [null]; codes [NotNull.user.name,NotNull.name,NotNull.java.lang.String,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [user.name,name]; arguments []; default message [name]]; default message [may not be null]],"path":"/user"}
에러마다 커스텀 json이 발생했으면 합니다.어떻게 하면 될까요?
모든 컨트롤러의 응답 메시지를 완전히 제어하려면ControllerAdvice
예를 들어, 이 변환 예는MethodArgumentNotValidException
커스텀 json 오브젝트로 변환합니다.
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import java.util.ArrayList;
import java.util.List;
import static org.springframework.http.HttpStatus.BAD_REQUEST;
/**
* Kudos http://www.petrikainulainen.net/programming/spring-framework/spring-from-the-trenches-adding-validation-to-a-rest-api/
*
*/
@Order(Ordered.HIGHEST_PRECEDENCE)
@ControllerAdvice
public class MethodArgumentNotValidExceptionHandler {
@ResponseStatus(BAD_REQUEST)
@ResponseBody
@ExceptionHandler(MethodArgumentNotValidException.class)
public Error methodArgumentNotValidException(MethodArgumentNotValidException ex) {
BindingResult result = ex.getBindingResult();
List<org.springframework.validation.FieldError> fieldErrors = result.getFieldErrors();
return processFieldErrors(fieldErrors);
}
private Error processFieldErrors(List<org.springframework.validation.FieldError> fieldErrors) {
Error error = new Error(BAD_REQUEST.value(), "validation error");
for (org.springframework.validation.FieldError fieldError: fieldErrors) {
error.addFieldError(fieldError.getField(), fieldError.getDefaultMessage());
}
return error;
}
static class Error {
private final int status;
private final String message;
private List<FieldError> fieldErrors = new ArrayList<>();
Error(int status, String message) {
this.status = status;
this.message = message;
}
public int getStatus() {
return status;
}
public String getMessage() {
return message;
}
public void addFieldError(String path, String message) {
FieldError error = new FieldError(path, message);
fieldErrors.add(error);
}
public List<FieldError> getFieldErrors() {
return fieldErrors;
}
}
}
Errors/BindingResult 개체를 사용하여 검증을 수행할 수 있습니다.컨트롤러 메서드에 Errors 인수를 추가하고 오류가 발견되면 오류 메시지를 사용자 지정합니다.
다음으로 검증에 실패했을 때 true를 반환하는 errors.hasErrors()의 예를 나타냅니다.
@RequestMapping(value = "/user", method = RequestMethod.POST)
@ResponseBody
public ResponseEntity<String> saveUser(@Valid @RequestBody User user, Errors errors) {
if (errors.hasErrors()) {
return new ResponseEntity(new ApiErrors(errors), HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<>(HttpStatus.OK);
}
오래된 질문인 건 알지만
그런데 우연히 발견했는데 꽤 괜찮은 기사가 있더군요. github에도 완벽한 예가 있어요.
기본적으로는@ControllerAdvice
봄철 문서에서 알 수 있듯이.
예를 들어 400개의 오류를 검출하려면 다음 중 하나의 함수를 덮어씁니다.
@ControllerAdvice
public class CustomRestExceptionHandler extends ResponseEntityExceptionHandler {
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(final MethodArgumentNotValidException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
logger.info(ex.getClass().getName());
//
final List<String> errors = new ArrayList<String>();
for (final FieldError error : ex.getBindingResult().getFieldErrors()) {
errors.add(error.getField() + ": " + error.getDefaultMessage());
}
for (final ObjectError error : ex.getBindingResult().getGlobalErrors()) {
errors.add(error.getObjectName() + ": " + error.getDefaultMessage());
}
final ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), errors);
return handleExceptionInternal(ex, apiError, headers, apiError.getStatus(), request);
}
}
(ApiError 클래스는 상태, 메시지, 오류를 유지하는 단순한 개체입니다.)
이를 위한 한 가지 방법은 엔티티 속성의 @NotNull 주석에 메시지를 추가하는 것입니다.컨트롤러 요청 본문에 @Valid 주석을 추가합니다.
DTO:
public class User {
@NotNull(message = "User name cannot be empty")
private String name;
@NotNull(message = "Password cannot be empty")
private String password;
//..
}
컨트롤러:
@RequestMapping(value = "/user", method = RequestMethod.POST)
public ResponseEntity<String> saveUser(@Valid @RequestBody User user) {
//..
return new ResponseEntity<>(HttpStatus.OK);
}
// Add one
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<List<YourErrorResponse>> handleException(MethodArgumentNotValidException ex) {
// Loop through FieldErrors in ex.getBindingResult();
// return *YourErrorReponse* filled using *fieldErrors*
}
@ControllerAdvice(annotations = RestController.class)
public class GlobalExceptionHandler implements ApplicationContextAware {
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.OK)
@ResponseBody
public ApplicationError validationException(MethodArgumentNotValidException e) {
e.printStackTrace();
return new ApplicationError(SysMessageEnum.MSG_005, e.getBindingResult().getAllErrors().get(0).getDefaultMessage());
}
}
이런 거 할 수 있어요.
@ExceptionHandler(value = MethodArgumentNotValidException.class)
protected ResponseEntity<Error> handleGlobalExceptions(MethodArgumentNotValidException ex,
WebRequest request) {
log.catching(ex);
return new ResponseEntity<>(createErrorResp(HttpStatus.BAD_REQUEST,
ex.getBindingResult().getFieldErrors().stream().map(err -> err.getDefaultMessage())
.collect(java.util.stream.Collectors.joining(", "))),
HttpStatus.BAD_REQUEST);
}
JSON 형식의 에러 메시지를 커스터마이즈 하려면 , 다음의 순서를 실행합니다.
- CommonErrorHandler라고 하는1개의 @Component를 만듭니다.
@Component
public class CommonErrorHandler {
public Map<String,Object> getFieldErrorResponse(BindingResult result){
Map<String, Object> fielderror = new HashMap<>();
List<FieldError>errors= result.getFieldErrors();
for (FieldError error : errors) {
fielderror.put(error.getField(), error.getDefaultMessage());
}return fielderror;
}
public ResponseEntity<Object> fieldErrorResponse(String message,Object fieldError){
Map<String, Object> map = new HashMap<>();
map.put("isSuccess", false);
map.put("data", null);
map.put("status", HttpStatus.BAD_REQUEST);
map.put("message", message);
map.put("timeStamp", DateUtils.getSysDate());
map.put("filedError", fieldError);
return new ResponseEntity<Object>(map,HttpStatus.BAD_REQUEST);
}
}
-- Invalid Exception 클래스 추가
public class InvalidDataException extends RuntimeException {
/**
* @author Ashok Parmar
*/
private static final long serialVersionUID = -4164793146536667139L;
private BindingResult result;
public InvalidDataException(BindingResult result) {
super();
this.setResult(result);
}
public BindingResult getResult() {
return result;
}
public void setResult(BindingResult result) {
this.result = result;
}
}
- @ControllerAdvice 클래스 소개
@ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler(InvalidDataException.class)
public ResponseEntity<?> invalidDataException(InvalidDataException ex, WebRequest request) {
List<FieldError> errors = ex.getResult().getFieldErrors();
for (FieldError error : errors) {
logger.error("Filed Name ::: " + error.getField() + "Error Message :::" + error.getDefaultMessage());
}
return commonErrorHandler.fieldErrorResponse("Error", commonErrorHandler.getFieldErrorResponse(ex.getResult()));
}
}
-- 컨트롤러에서 @Valid 및 through 예외와 함께 사용합니다.
public AnyBeans update(**@Valid** @RequestBody AnyBeans anyBeans ,
BindingResult result) {
AnyBeans resultStr = null;
if (result.hasErrors()) {
**throw new InvalidDataException(result);**
} else {
resultStr = anyBeansService.(anyBeans );
return resultStr;
}
}
-- 출력은 JSON 형식입니다.
{
"timeStamp": 1590500231932,
"data": null,
"message": "Error",
"isSuccess": false,
"status": "BAD_REQUEST",
"filedError": {
"name": "Name is mandatory"
}
}
이것이 효과가 있기를 바랍니다. :-D
@ControllerAdvice
@RestController
public class CustomizedResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(
MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
// ex.getBindingResult(): extract the bind result for default message.
String errorResult = ex.getBindingResult().toString();
CustomizedExceptionHandlerResponse exceptionResponse = new CustomizedExceptionHandlerResponse(
errorResult, new Date(), request.getDescription(false));
return new ResponseEntity<>(exceptionResponse, HttpStatus.BAD_REQUEST);
}
}
class CustomizedExceptionHandlerResponse {
private String message;
private String status;
private Date timestamp;
// constuctor, setters, getters...
}
이 코드를 사용하여 오류를 반복하고 커스텀에러 메시지를 작성할 수 있습니다.
import lombok.Data;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import javax.validation.ConstraintViolation;
import java.util.List;
import java.util.stream.Collectors;
@ControllerAdvice
public class ExceptionHandlerController {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorDto> handleException(MethodArgumentNotValidException ex) {
ErrorDto dto = new ErrorDto(HttpStatus.BAD_REQUEST, "Validation error");
dto.setDetailedMessages(ex.getBindingResult().getAllErrors().stream()
.map(err -> err.unwrap(ConstraintViolation.class))
.map(err -> String.format("'%s' %s", err.getPropertyPath(), err.getMessage()))
.collect(Collectors.toList()));
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(dto);
}
@Data
public static class ErrorDto {
private final int status;
private final String error;
private final String message;
private List<String> detailedMessages;
public ErrorDto(HttpStatus httpStatus, String message) {
status = httpStatus.value();
error = httpStatus.getReasonPhrase();
this.message = message;
}
}
}
그러면 오류가 발생한 경우 다음과 같은 응답이 나타납니다.
{
"status": 400,
"error": "Bad Request",
"message": "Validation error",
"detailedMessages": [
"'yourField' should not be empty."
]
}
정보도 추가해 주세요.그냥 사용하면@Valid
, 당신은 캐치할 필요가 있다.BindException
를 사용하는 경우@Valid @RequestBody
또 만나MethodArgumentNotValidException
일부 소스:
HandlerMethodArgumentResolverComposite.getArgumentResolver(MethodParameter parameter):129
- 어떤 Handler Method Argument Resolver가 이러한 파라미터를 지원하는지 검색합니다. RequestResponseBodyMethodProcessor.supportsParameter(MethodParameter parameter)
- 파라미터에 주석이 있는 경우 true를 반환합니다. @RequestBody
RequestResponseBodyMethodProcessor:139
- Method Argument Not Valid Exception을 슬로우합니다. ModelAttributeMethodProcessor:164
- Bind Exception을 슬로우합니다.
언급URL : https://stackoverflow.com/questions/33663801/how-do-i-customize-default-error-message-from-spring-valid-validation
'programing' 카테고리의 다른 글
Angular를 사용하는 글로벌 Ajax 오류 처리기JS (0) | 2023.03.18 |
---|---|
Perl에서 단순 해시를 json으로 변환하는 방법 (0) | 2023.03.18 |
ReactJs - "If" 구성 요소 생성 중...좋은 생각? (0) | 2023.03.18 |
JSON을 XML로 변환하거나 XML을 JSON으로 변환하는 방법 (0) | 2023.03.18 |
rspec 요청 사양에서 요청 헤더를 설정하려면 어떻게 해야 합니다. (0) | 2023.03.18 |