If you prepare a handler class with @ControllerAdvice , Handling of validation error of each controller can be handled at once.
Java 11
SpringBoot 2.3.3
The following is an explanation of the REST controller that acquires a list of users by specifying search parameters. Input validation of the request is provided, and when a validation error is detected, 400 error is returned together with the specified response body.
Add @Validated
to the argument so that validation is performed.
Handling at the time of error is not particularly performed here.
@RestController
@RequiredArgsConstructor
public class UserController {
@NonNull
private final UserService userService;
@NonNull
private final GetUsersQueryValidator getUsersQueryValidator;
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.addValidators(getUsersQueryValidator);
}
/**
*Get user information by specifying search conditions
*
* @param getUsersQuery Search Criteria Query Parameters
* @return Searched user information
*/
@GetMapping(value = "/users")
ResponseEntity<List<UserDto>> getUsers(@Validated GetUsersQuery getUsersQuery) {
SearchUsersCondition searchUsersCondition = new SearchUsersCondition();
searchUsersCondition.setName(getUsersQuery.getName());
searchUsersCondition.setLowerLimitAge(getUsersQuery.getLowerLimitAge());
searchUsersCondition.setUpperLimitAge(getUsersQuery.getUpperLimitAge());
return ResponseEntity.ok(userService.searchUsers(searchUsersCondition));
}
}
The class to which the query parameters of the request are bound.
@NotBlank
and @NotNull
are added to each field so that unary validation is performed.
/**
*Query parameters that specify user search criteria
*/
@Data
public class GetUsersQuery {
/**
*username
*/
@NotBlank
private String name;
/**
*Minimum age
*/
@NotNull
private Integer lowerLimitAge;
/**
*Maximum age
*/
@NotNull
private Integer upperLimitAge;
}
An error occurs when the lower limit age of the query parameter exceeds the upper limit age.
/**
* {@link GetUsersQuery}Correlation validator
*/
@Component
public class GetUsersQueryValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
return GetUsersQuery.class.isAssignableFrom(clazz);
}
/**
*Validation implementation
*
* @param target Validation target
* @param errors Detected errors
*/
@Override
public void validate(Object target, Errors errors) {
//No correlation validation is performed if a unary error occurs in either the upper age limit or the lower limit age.
if (errors.hasFieldErrors("lowerLimitAge") || errors.hasFieldErrors("upperLimitAge")) {
return;
}
GetUsersQuery getUsersQuery = GetUsersQuery.class.cast(target);
int lowerLimitAge = getUsersQuery.getLowerLimitAge();
int upperLimitAge = getUsersQuery.getUpperLimitAge();
//If the upper age limit does not exceed the lower age limit, an error will occur.
if (lowerLimitAge >= upperLimitAge) {
errors.reject("reverseLimitAge");
}
}
}
When an error occurs, an object of this type is stored in the response body.
/**
*Error information to be set in the request body
*/
@Data
public class ApiError implements Serializable {
private static final long serialVersionUID = 1L;
private String message;
}
Prepare an exception handler class with @ControllerAdvice
as shown below.
/**
*Handler for exceptions raised on the controller
*/
@ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
@Autowired
MessageSource messageSource;
/**
* {@link BindException}Handling
*
* @param bindException {@link BindException}
* @param httpHeaders {@link HttpHeaders}
* @param httpStatus {@link HttpStatus}
* @param webRequest {@link WebRequest}
* @return Response to client
*/
@Override
protected ResponseEntity<Object> handleBindException(
BindException bindException,
HttpHeaders httpHeaders,
HttpStatus httpStatus,
WebRequest webRequest
) {
//Error list stored in the response body
List<ApiError> apiErrorList = new ArrayList<>();
List<ObjectError> objectErrorList = bindException.getAllErrors();
for (ObjectError objectError : objectErrorList) {
//Get message from error code
String message = messageSource.getMessage(objectError, webRequest.getLocale());
//Create an error object for the response body and store it in the list
ApiError apiError = new ApiError();
apiError.setMessage(message);
apiErrorList.add(apiError);
}
return new ResponseEntity<>(apiErrorList, httpHeaders, httpStatus);
}
}
When a validation error occurs, the controller throws a BindException
that stores the error information.
In the class with @ControllerAdvice
, implement the process that you want to apply to each controller. By inheriting ResponseEntityExceptionHandler
and overriding the handleBindException
method, you can freely customize the response at the time of validation error.
Here, it is customized as follows.
--Specify the response body as ʻApiError type. --Converted error code of ʻobjectError
to error message.
The error code is stored in ʻobjectError` in the following format.
Unary validation: "annotation name + class name (camel case) + field name" Correlation validation: "Error code set by correlation validator + class name (camel case)"
If you prepare messages.properties
as follows, it will be converted into a message.
messages.properties
NotBlank.getUsersQuery.name=Entering a name is mandatory.
NotNull.getUsersQuery.lowerLimitAge=Entering the minimum age is mandatory.
NotNull.getUsersQuery.upperLimitAge=Entering the maximum age is mandatory.
reverseLimitAge.getUsersQuery=Specify a value larger than the lower limit age for the upper limit age.
Recommended Posts