[JAVA] Filtrer le résultat de BindingResult [Spring]

Public cible

-J'implémente la validation dans le framework Spring, mais je veux filtrer le résultat de BindingResult! ・ Je souhaite utiliser un formulaire commun pour les nouveaux et les mises à jour!

Confirmation de l'objet

View

コメント 2020-05-16 103732.png

En utilisant simplement la balise Spring, userForm est défini dans modelAttribute, et la balise d'entrée et la balise d'étiquette sont écrites. Le côté mise à jour a exactement la même structure.

Form

UserForm.java



@Data
@ConfirmMail(field = "mail", groups = MailGroup3.class)
@ConfirmPassword(field = "password", groups = PasswordGroup4.class)
@CheckBirthday(field = "birthday", groups = ValidGroup2.class)
public class UserForm {

	private String id;

	@NotBlank(groups = ValidGroup1.class)
	@Size(min = 6, max = 20, groups = ValidGroup2.class)
	@Pattern(regexp = "^[a-zA-Z0-9]+$", groups = ValidGroup3.class)
	private String account;

	@NotBlank(groups = PasswordGroup1.class)
	@Size(min = 6, max = 12, groups = PasswordGroup2.class)
	@Pattern(regexp = "^[a-zA-Z0-9!-/:-@¥[-`{-~]]+$", groups = PasswordGroup3.class)
	private String password;

	@NotBlank(groups = PasswordGroup1.class)
	@Size(min = 6, max = 12, groups = PasswordGroup2.class)
	@Pattern(regexp = "^[a-zA-Z0-9!-/:-@¥[-`{-~]]+$", groups = PasswordGroup3.class)
	private String confirmPassword;

	@NotBlank(groups = ValidGroup1.class)
	@Size(min = 1, max = 10, groups = ValidGroup2.class)
	private String name;

	@NotBlank(groups = ValidGroup1.class)
	private String birthday;

	@NotBlank(groups = MailGroup1.class)
	@Email(groups = MailGroup2.class)
	private String mail;

	@NotBlank(groups = MailGroup1.class)
	@Email(groups = MailGroup2.class)
	private String confirmMail;

	private String roleName;

}

Des espaces, des expressions régulières, des mots de passe de confirmation, des adresses e-mail de confirmation et d'autres validations sont définis pour chaque élément. Les champs à filtrer sont séparés et il est préférable de spécifier GroupOrder. .. ..

Controller

SignupController


	@PostMapping("/signup")
	String createUser(@ModelAttribute("userForm") @Validated({ GroupOrder.class, PasswordGroupOrder.class, MailGroupOrder.class })  UserForm userForm,
			BindingResult result, RedirectAttributes redirectAttributes) {

		if(result.hasErrors()) {
			return "users/signup/new";
		}
		mailAuthUserService.authUserbyMail(userForm);
		redirectAttributes.addFlashAttribute("resultMessage", "Un e-mail a été envoyé. Veuillez vérifier votre boîte aux lettres.");
		return "redirect:/login";
}

Lorsque vous envisagez de les détourner du côté de la mise à jour, comme un problème (Supposons que vous apportiez les informations telles qu'elles proviennent de la base de données)

-Lors de la mise à jour du mot de passe, le mot de passe est haché de manière irréversible, de sorte que les informations apportées par la base de données ne peuvent pas être saisies telles quelles dans le formulaire côté mise à jour (celui haché au moment de la mise à jour est haché). Parce que ce sera fait). Le mot de passe doit être vide. -Lorsque vous ne souhaitez pas mettre à jour, les éléments du mot de passe de confirmation et de l'adresse e-mail de confirmation sont vides, la validation est donc appliquée. Il est difficile de placer les éléments un par un.

La validation fait donc obstacle.

コメント 2020-05-16 111346.png

Contre-mesures

Est-il possible de supprimer certains éléments d'erreur de BindinResult?

Selon l'enseignant de Google, il semble que la méthode pour supprimer l'élément d'erreur n'est pas définie dans BindinResult, donc cela ne peut pas être fait. Si vous `` redéfinissez l'objet '' au lieu de l'effacer, vous pourrez ainsi l'effacer!

Méthode de filtrage

UserController.java


	private BindingResult filteringBindingResult(BindingResult result, UserForm userForm) {

		UserDto userDto = findById(userForm.getId()); //· · · 1
		BindingResult tmpResult = new BeanPropertyBindingResult(userForm, "userForm");//・ ・ ・ 2

		if(StringUtils.isEmpty(userForm.getPassword()) && userDto.getMail().equals(userForm.getMail())) { //・ ・ ・ 3

			for(FieldError fieldError : result.getFieldErrors()) { //・ ・ ・ 4
				if(fieldError.getField().equals("confirmMail") ||
						fieldError.getField().equals("confirmPassword") ||
						fieldError.getField().equals("password")) {
					continue;
				}
				tmpResult.addError(fieldError);
			}

			return tmpResult;
		}

		if(StringUtils.isEmpty(userForm.getPassword())) {

			for(FieldError fieldError : result.getFieldErrors()) {
				if(fieldError.getField().equals("confirmPassword") ||
						fieldError.getField().equals("password")) {
					continue;
				}
				tmpResult.addError(fieldError);
			}

			return tmpResult;
		}

		if(userDto.getMail().equals(userForm.getMail())) {

			for(FieldError fieldError : result.getFieldErrors()) {
				if(fieldError.getField().equals("confirmMail")) {
					continue;
				}
				tmpResult.addError(fieldError);
			}

			return tmpResult;
		}

		return result;
	}

Arguments: Résultat BindingResult (stocke le résultat de l'erreur), ʻUserForm userForm (élément entré du côté de la vue) `

Valeur de retour ・ ・ ・ BindingResult

1 ... Défini pour extraire les informations utilisateur de la base de données et vérifier si l'adresse e-mail a changé. 2 ... Puisque BindingResult est une interface, il n'a pas d'implémentation. BeanPropertyBindingResult est l'implémentation par défaut de BindingResult. Par conséquent, il est nécessaire d'attribuer un élément d'erreur filtré à cet objet, définissez-le donc. Springframework 3 ... Dans cette implémentation, nous voulons utiliser la validation quand elle doit être mise à jour, alors vérifiez si le mot de passe est vide et que l'adresse e-mail n'a pas changé, et si true, filtrez sur la validation. Lorsque est multiplié par '' et est faux, l'implémentation est telle que multiplié par la validation normale ''. 4 ... Avec le contrôleur, les BindingResults reçus sont retirés un par un, ceux qui correspondent à l'élément à filtrer sont sautés avec continue et les autres sont affectés à tmpResult.

Mise à jour du 10/06/2020

UserController.java


	private BindingResult filteringBindingResult(BindingResult result, UserForm userForm) throws Exception {

		final BindingResult newResult = new BeanPropertyBindingResult(userForm, "userForm");
		Set<FieldError> allErrors = new HashSet<>();
		result.getFieldErrors().forEach(x -> allErrors.add(x));

		if (StringUtils.isEmpty(userForm.getPassword()) && StringUtils.isEmpty(userForm.getConfirmPassword())) {
			allErrors.removeIf(x -> x.getField().equals("password") || x.getField().equals("confirmPassword"));
		}

		UserDto userDto = userService.findById(userForm.getId());
		if (userDto.getMail().equals(userForm.getMail()) && StringUtils.isEmpty(userForm.getConfirmMail())) {
			allErrors.removeIf(x -> x.getField().equals("mail") || x.getField().equals("confirmMail"));
		}

		allErrors.forEach(x -> newResult.addError(x));

		if (result.getFieldErrorCount() == newResult.getFieldErrorCount()) {
			return result;
		} else {
			return newResult;
		}
	}

Le résultat BindingResult est supprimé uniquement lorsqu'il est actualisé à l'aide de l'expression lambda et lorsque le mot de passe de confirmation et le mot de passe sont vides et lorsque l'adresse e-mail n'a pas changé et que l'adresse e-mail de confirmation est vide. ..

Controller

UserController.java


	@PostMapping("/{id}/edit")
	String edit(Model model,
			@Validated({ GroupOrder.class, PasswordGroupOrder.class, MailGroupOrder.class })  UserForm userForm,
			BindingResult result) {

		BindingResult filteringBindingResult = filteringBindingResult(result, userForm); //· · · 1

		if(filteringBindingResult.hasErrors()) {
			model.addAttribute("org.springframework.validation.BindingResult.userForm", filteringBindingResult); //・ ・ ・ 2
			return "users/edit";
		}

		userService.update(userForm);
		return "redirect:/user/mypage";

	}

1 ... La valeur de retour de la méthode définie précédemment est stockée dans filteringBindingResult. 2 ... J'ai eu un moment très difficile ici, mais au début je me demandais si je pouvais l'implémenter en douceur en réécrivant la référence du résultat BindingResult, mais apparemment, lorsque ce contrôleur a été appelé, il est devenu un modèle. Le résultat de BindingResult avant d'appliquer le filtre a été stocké. Vous devez donc écraser cet objet. "org.springframework.validation.BindingResult.userForm a imprimé et débogué le modèle lui-même ...

Impressions

Honnêtement, je ne sais pas si c'est la meilleure pratique. Il peut être plus facile de voir lorsque vous l'examinez plus tard si vous séparez simplement le formulaire pour la mise à jour et le nouvel enregistrement, mais en rendant le formulaire commun, vous pouvez également rendre le message d'erreur commun, je vais donc utiliser cette méthode A été choisi. Je serais reconnaissant si quelqu'un pouvait être utile.

2020/06/09 Modifier

Si le @ GroupSequence de la validation est identique, il y avait un problème que la validation n'a pas été interceptée lorsque d'autres champs étaient en erreur. Cela est dû au fait que le BindingResult filtré ne sera pas sorti après ValidGroup1 en raison de la spécification selon laquelle ValidGroup2,3,4 ci-dessous ne sera pas généré s'il est intercepté dans ValidGroup1 de GroupOrder. Par conséquent, il est nécessaire de préparer et d'appliquer @ GroupSequence séparément.

Recommended Posts

Filtrer le résultat de BindingResult [Spring]
Filtrer les fluctuations des données brutes
La validation de printemps était importante dans l'ordre de Form et BindingResult
L'histoire de la rencontre avec l'annotation personnalisée Spring
Obtenez le résultat de POST en Java
À propos de l'affichage initial de Spring Framework
[Java] [Spring] Tester le comportement de l'enregistreur
À propos du guide de démarrage officiel de Spring Framework
L'histoire de la montée de la série Spring Boot 1.5 à la série 2.1
Vérifions la sensation de Spring Boot + Swagger 2.0
Obtenez la partie Body de HttpResponse avec Spring Filter
Comment afficher le résultat du remplissage du formulaire
Le nom officiel de Spring MVC est Spring Web MVC
[Spring] Pièges de BeanUtils.copyProperties
[Pour les débutants] DI ~ Les bases de DI et DI au printemps ~
N'arrondissez pas facilement le résultat d'un calcul fractionnaire
Accédez au h2db intégré de Spring Boot avec jdbcTemplate
05. J'ai essayé de supprimer la source de Spring Boot
J'ai essayé de réduire la capacité de Spring Boot
Jugement du calendrier
Le monde de Clara-Rules (4)
Le monde de Clara-Rules (1)
À propos de DI of Spring ②
Le monde de Clara-Rules (3)
Jusqu'à l'utilisation de Spring Data et JPA Part 2
Le monde de Clara-Rules (5)
Jusqu'à l'utilisation de Spring Data et JPA Part 1
L'idée du tri rapide
Une note de révision de l'interface Spring Framework Resource
Vérifiez le résultat de l'inférence de paramètre de type générique avec JShell
Vérifiez le comportement de include, exclude et ExhaustedRetryException de Spring Retry
Contrôlez le flux de traitement Spring Batch avec JavaConfig.
Un record d'étude du Spring Framework à partir de zéro
Présentation de Spring AOP
L'idée de jQuery
L'histoire de la montée de Spring Boot de la série 1.5 à la série 2.1 part2
Mettez le fichier dans les propriétés de string avec la configuration spring xml
Une histoire remplie des bases de Spring Boot (résolu)
Site où vous pouvez voir la relation de version de spring (?)
Saisissons l'image de fonctionnement (atmosphère) du conteneur DI de Spring
À propos de la gestion de Null
Surveillance Docker-expliquant les bases des bases-
Note de résultat d'exécution de String.substring
À propos de la description de Docker-compose.yml
Le jeu d'instancier java.lang.Void
Valeur médiane de trois valeurs
L'illusion de l'orientation objet
Essayez le tutoriel Spring WebFlux
Je veux comprendre le flux des paramètres de demande de traitement Spring
Obtenez une instance proxy du composant lui-même dans Spring Boot
Partie 4: Personnalisez le comportement de la connexion OAuth 2.0 prise en charge par Spring Security 5
Obtenez les résultats Flux de Spring Web Flux de JS avec Fetch API
Voir le comportement des mises à jour d'entités avec Spring Boot + Spring Data JPA
Je veux contrôler le message d'erreur par défaut de Spring Boot