[JAVA] Gestion collective des erreurs de validation Spring avec @ControllerAdvice

Conclusion

Si vous préparez une classe de gestionnaire avec @ControllerAdvice , Le traitement des erreurs de validation de chaque contrôleur peut être traité à la fois.

environnement

Java 11 SpringBoot 2.3.3

Commentaire

Ce qui suit est une explication du contrôleur REST qui acquiert une liste d'utilisateurs en spécifiant des paramètres de recherche. Une validation d'entrée de la demande est fournie, et lorsqu'une erreur de validation est détectée, une erreur 400 est renvoyée avec un corps de réponse prédéterminé.

Classe de contrôleur

Ajoutez @ Validated à l'argument pour que la validation soit effectuée. La manipulation au moment de l'erreur n'est pas particulièrement effectuée ici.


@RestController
@RequiredArgsConstructor
public class UserController {

    @NonNull
    private final UserService userService;

    @NonNull
    private final GetUsersQueryValidator getUsersQueryValidator;

    @InitBinder
    public void initBinder(WebDataBinder binder) {
        binder.addValidators(getUsersQueryValidator);
    }

    /**
     *Obtenir des informations sur l'utilisateur en spécifiant les conditions de recherche
     *
     * @param getUsersQuery Critères de recherche Paramètres de requête
     * @retour Informations sur l'utilisateur recherché
     */
    @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));
    }
}

Classe de paramètre de requête

La classe à laquelle les paramètres de requête de la demande sont liés. «@ NotBlank» et «@ NotNull» sont ajoutés à chaque champ afin que la validation à un seul terme soit effectuée.

/**
 *Paramètres de requête qui spécifient les conditions de recherche des utilisateurs
 */
@Data
public class GetUsersQuery {

    /**
     *Nom d'utilisateur
     */
    @NotBlank
    private String name;

    /**
     *Âge minimum
     */
    @NotNull
    private Integer lowerLimitAge;

    /**
     *Age maximum
     */
    @NotNull
    private Integer upperLimitAge;

}

Classe de corrélation Varidata

Une erreur se produit lorsque l'âge limite inférieur du paramètre de requête dépasse l'âge limite supérieur.

/**
 * {@link GetUsersQuery}Corrélation Varidata
 */
@Component
public class GetUsersQueryValidator implements Validator {

    @Override
    public boolean supports(Class<?> clazz) {
        return GetUsersQuery.class.isAssignableFrom(clazz);
    }

    /**
     *Mise en œuvre de la validation
     *
     * @Param target Cible de validation
     * @erreurs de paramètres Erreurs détectées
     */
    @Override
    public void validate(Object target, Errors errors) {

        //La validation de la corrélation n'est pas effectuée si une erreur de terme unique se produit dans l'âge limite supérieur ou inférieur.
        if (errors.hasFieldErrors("lowerLimitAge") || errors.hasFieldErrors("upperLimitAge")) {
            return;
        }

        GetUsersQuery getUsersQuery = GetUsersQuery.class.cast(target);

        int lowerLimitAge = getUsersQuery.getLowerLimitAge();
        int upperLimitAge = getUsersQuery.getUpperLimitAge();

        //Si l'âge limite supérieur ne dépasse pas l'âge limite inférieur, une erreur se produira.
        if (lowerLimitAge >= upperLimitAge) {
            errors.reject("reverseLimitAge");
        }
    }
}

Classe d'erreur pour le corps de la réponse

Lorsqu'une erreur se produit, un objet de ce type est stocké dans le corps de la réponse.

/**
 *Informations d'erreur à définir dans le corps de la requête
 */
@Data
public class ApiError implements Serializable {

    private static final long serialVersionUID = 1L;

    private String message;
}

Classe de gestionnaire d'exception

Préparez une classe de gestionnaire d'exceptions avec @ ControllerAdvice comme indiqué ci-dessous.


/**
 *Gestionnaire pour les exceptions levées sur le contrôleur
 */
@ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {

    @Autowired
    MessageSource messageSource;

    /**
     * {@link BindException}Manipulation
     *
     * @param bindException {@link BindException}
     * @param httpHeaders   {@link HttpHeaders}
     * @param httpStatus    {@link HttpStatus}
     * @param webRequest    {@link WebRequest}
     * @retourner la réponse au client
     */
    @Override
    protected ResponseEntity<Object> handleBindException(
            BindException bindException,
            HttpHeaders httpHeaders,
            HttpStatus httpStatus,
            WebRequest webRequest
    ) {
        //Liste d'erreurs stockée dans le corps de la réponse
        List<ApiError> apiErrorList = new ArrayList<>();

        List<ObjectError> objectErrorList = bindException.getAllErrors();

        for (ObjectError objectError : objectErrorList) {

            //Obtenir le message du code d'erreur
            String message = messageSource.getMessage(objectError, webRequest.getLocale());

            //Créez un objet d'erreur pour le corps de la réponse et stockez-le dans la liste
            ApiError apiError = new ApiError();
            apiError.setMessage(message);
            apiErrorList.add(apiError);
        }

        return new ResponseEntity<>(apiErrorList, httpHeaders, httpStatus);
    }
}

Lorsqu'une erreur de validation se produit, le contrôleur lance une BindException qui stocke les informations d'erreur. Dans la classe avec @ ControllerAdvice, implémentez le processus que vous souhaitez appliquer à chaque contrôleur. En héritant de ResponseEntityExceptionHandler et en remplaçant la méthode handleBindException, vous pouvez librement personnaliser la réponse au moment de l'erreur de validation.

Ici, il est personnalisé comme suit.

--Spécifiez le corps de la réponse comme type ʻApiError. --Converti du code d'erreur de ʻobjectError en message d'erreur.

Le code d'erreur est stocké dans ʻobjectError` au format suivant.

Validation d'un seul terme: "Nom d'annotation + nom de classe (cas camel) + nom de champ" Validation de la corrélation: "Code d'erreur défini dans la validation de la corrélation + nom de la classe (cas de chameau)"

Si vous préparez messages.properties comme suit, il sera converti en message.

messages.properties

NotBlank.getUsersQuery.name=La saisie d'un nom est obligatoire.
NotNull.getUsersQuery.lowerLimitAge=La saisie de l'âge minimum est obligatoire.
NotNull.getUsersQuery.upperLimitAge=La saisie de l'âge maximum est obligatoire.
reverseLimitAge.getUsersQuery=Spécifiez une valeur supérieure à l'âge limite inférieur pour l'âge limite supérieur.

Recommended Posts

Gestion collective des erreurs de validation Spring avec @ControllerAdvice
Validation personnalisée avec Spring
Obtenez des résultats de validation avec Spring Boot
Test de validation de classe de formulaire avec Spring Boot
Obtenez la partie Body de HttpResponse avec Spring Filter
[Java] Article pour ajouter une validation avec Spring Boot 2.3.1.
J'ai besoin de la validation de Spring Data pour Pageable ~
Personnalisation de la validation
Compatibilité de Spring JDBC et My Batis avec Spring Data JDBC (provisoire)
Accédez au h2db intégré de Spring Boot avec jdbcTemplate
Créer Restapi avec Spring Boot (jusqu'à l'exécution de l'application)
Comment démarrer par environnement avec Spring Boot de Maven
Contrôlez le flux de traitement Spring Batch avec JavaConfig.