web-dev-qa-db-fra.com

Spring Web MVC - valider les paramètres de demande individuels

J'exécute une webapp dans Spring Web MVC 3.0 et j'ai un certain nombre de méthodes de contrôleur dont les signatures sont à peu près comme suit:

@RequestMapping(value = "/{level1}/{level2}/foo", method = RequestMethod.POST)
public ModelAndView createFoo(@PathVariable long level1,
        @PathVariable long level2,
        @RequestParam("foo_name") String fooname,
        @RequestParam(value = "description", required = false) String description);

Je voudrais ajouter une validation - par exemple, description devrait être limité à une certaine longueur ou fooname ne devrait contenir que certains caractères. Si cette validation échoue, je veux retourner un message à l'utilisateur plutôt que de simplement lancer une exception non vérifiée (ce qui se produirait de toute façon si je laissais les données percoler vers la couche DAO). Je connais JSR303 mais je n'ai pas travaillé avec et je ne comprends pas très bien comment l'appliquer dans un contexte Spring.

D'après ce que je comprends, une autre option serait de lier le @RequestBody à un objet de domaine entier et y ajouter des contraintes de validation, mais actuellement mon code est configuré pour accepter des paramètres individuels comme indiqué ci-dessus.

Quelle est la façon la plus simple d'appliquer la validation aux paramètres d'entrée à l'aide de cette approche?

48
Dan

Il n'y a rien de prévu pour cela, pasencore de toute façon. Avec les versions actuelles, vous devrez toujours utiliser WebDataBinder pour lier vos paramètres à un objet si vous souhaitez une validation automagique. Cela vaut la peine d'apprendre à faire si vous utilisez SpringMVC, même si ce n'est pas votre premier choix pour cette tâche.

Cela ressemble à ceci:

public ModelAndView createFoo(@PathVariable long level1,
        @PathVariable long level2,
        @Valid @ModelAttribute() FooWrapper fooWrapper,
        BindingResult errors) {
  if (errors.hasErrors() {
     //handle errors, can just return if using Spring form:error tags.
  }
}

public static class FooWrapper {
  @NotNull
  @Size(max=32)
  private String fooName;
  private String description;
//getset
}

Si vous avez Hibernate Validator 4 ou une version ultérieure sur votre chemin de classe et utilisez la configuration du répartiteur par défaut, cela devrait "fonctionner".

Modification depuis que les commentaires devenaient un peu gros:

Tout objet qui se trouve dans votre signature de méthode et qui n'est pas l'un des "attendus" que Spring sait injecter, comme HttpRequest, ModelMap, etc., obtiendra les données liées. Ceci est accompli pour les cas simples simplement en faisant correspondre les noms des paramètres de la demande avec les noms des propriétés du bean et en appelant les setters. Le @ModelAttribute il y a juste un truc de style personnel, dans ce cas ça ne fait rien. L'intégration du JSR-303 avec @Valid sur un paramètre de méthode est connectée via WebDataBinder. Si tu utilises @RequestBody, vous utilisez un marshaller d'objet basé sur le type de contenu déterminé par spring pour le corps de la requête (généralement juste à partir de l'en-tête http.) La servlet du répartiteur (AnnotationMethodHandlerAdapter vraiment) n'a pas de moyen pour ' retourner le commutateur de validation 'pour tout marshaller arbitraire. Il transmet simplement le contenu de la demande Web au convertisseur de messages et récupère un objet. Aucun objet BindingResult n'est généré, il n'y a donc nulle part où définir les erreurs de toute façon.

Vous pouvez toujours injecter votre validateur dans le contrôleur et l'exécuter sur l'objet que vous obtenez, il n'a tout simplement pas l'intégration magique avec le @Valid sur le paramètre de requête remplissant le BindingResult pour vous.

29
Affe

Cela semble être possible maintenant (essayé avec Spring 4.1.2), voir https://raymondhlee.wordpress.com/2015/08/29/validating-spring-mvc-request-mapping-method-parameters/

Extrait de la page ci-dessus:

  1. Ajoutez MethodValidationPostProcessor à la classe Spring @Configuration:

    @Bean
    public MethodValidationPostProcessor methodValidationPostProcessor() {
        return new MethodValidationPostProcessor();
    }
    
  2. Ajouter @Validated à la classe de contrôleur

  3. Utilisez @Size juste avant @RequestParam

    @RequestMapping("/hi")
    public String sayHi(@Size(max = 10, message = "name should at most 10 characters long") @RequestParam("name") String name) {
        return "Hi " + name;
    

    }

  4. Gérer ConstraintViolationException dans une méthode @ExceptionHandler

38
anre

Si vous avez plusieurs paramètres de demande qui doivent être validés (avec Http [~ # ~] obtenez [~ # ~] ou [~ # ~] poster [~ # ~] ). Vous pourriez aussi bien créer une classe de modèle personnalisé et utiliser @ Valid avec @ ModelAttribute pour valider les paramètres. De cette façon, vous pouvez utiliser Hibernate Validator ou javax.validator api pour valider les paramètres. Ca fait plutot comme ca:

Méthode de demande:

@RequestMapping(value="/doSomething", method=RequestMethod.GET)
public Model dosomething(@Valid @ModelAttribute ModelRequest modelRequest, BindingResult result, Model model) {

    if (result.hasErrors()) {
        throw new SomeException("invalid request params");
    }

    //to access the request params
    modelRequest.getFirstParam();
    modelRequest.getSecondParam();

    ...   
}

Classe ModelRequest :

class ModelRequest {

    @NotNull
    private String firstParam;

    @Size(min = 1, max = 10, message = "You messed up!")
    private String secondParam;

    //Setters and getters

    public void setFirstParam (String firstParam) {
        this.firstParam = firstParam;
    }

    public String getFirstParam() {
        return firstParam;
    }

    ...
}

J'espère que ça t'as aidé.

17
MasterV