Avec Spring, puis-je créer une variable de chemin facultative?

187

Avec Spring 3.0, puis-je avoir une variable de chemin facultative?

Par exemple

@RequestMapping(value = "/json/{type}", method = RequestMethod.GET)
public @ResponseBody TestBean testAjax(
        HttpServletRequest req,
        @PathVariable String type,
        @RequestParam("track") String track) {
    return new TestBean();
}

Ici, je voudrais /json/abcou /jsonappeler la même méthode.
Une solution de contournement évidente à déclarer typecomme paramètre de demande:

@RequestMapping(value = "/json", method = RequestMethod.GET)
public @ResponseBody TestBean testAjax(
        HttpServletRequest req,
        @RequestParam(value = "type", required = false) String type,
        @RequestParam("track") String track) {
    return new TestBean();
}

et puis /json?type=abc&track=aaou /json?track=rrfonctionnera

Shamik
la source

Réponses:

194

Vous ne pouvez pas avoir de variables de chemin facultatives, mais vous pouvez avoir deux méthodes de contrôleur qui appellent le même code de service:

@RequestMapping(value = "/json/{type}", method = RequestMethod.GET)
public @ResponseBody TestBean typedTestBean(
        HttpServletRequest req,
        @PathVariable String type,
        @RequestParam("track") String track) {
    return getTestBean(type);
}

@RequestMapping(value = "/json", method = RequestMethod.GET)
public @ResponseBody TestBean testBean(
        HttpServletRequest req,
        @RequestParam("track") String track) {
    return getTestBean();
}
Earldouglas
la source
5
@Shamik: C'est une raison impérieuse de ne pas utiliser de variables de chemin, à mon avis. La prolifération combinatoire peut rapidement devenir incontrôlable.
skaffman
9
En fait, pas parce que le chemin ne peut pas être aussi complexe tout en étant rempli de composants optionnels. Si vous avez plus d'un ou au maximum deux éléments de chemin optionnels, vous devriez sérieusement envisager d'en changer quelques-uns pour demander des paramètres.
Patrick Cornelissen
1
Et pour certaines personnes, demander à la deuxième méthode de contrôleur d'appeler la première méthode de contrôleur peut également fonctionner si, par exemple, le paramètre différent peut être fourni par d'autres moyens
chrismarx
3
Veuillez envisager de mettre à jour votre réponse, au lieu de créer deux méthodes de contrôleur dans la nouvelle version de Spring, nous pouvons simplement utiliser @RequestMappingavec deux valeurs comme dans: stackoverflow.com/questions/17821731
...
omg comment comptez-vous maintenir ces terminaux? Et si au lieu d'une seule variable de chemin, nous avons 5, faites le calcul pour moi, combien de points de terminaison feriez-vous? S'il vous plaît faites-moi une faveur et remplacez- @PathVariablele@RequestParam
Guilherme Alencar
114

Si vous utilisez Spring 4.1 et Java 8 , vous pouvez utiliser java.util.Optionalqui est pris en charge @RequestParam, @PathVariable, @RequestHeaderet @MatrixVariabledans Spring MVC -

@RequestMapping(value = {"/json/{type}", "/json" }, method = RequestMethod.GET)
public @ResponseBody TestBean typedTestBean(
    @PathVariable Optional<String> type,
    @RequestParam("track") String track) {      
    if (type.isPresent()) {
        //type.get() will return type value
        //corresponds to path "/json/{type}"
    } else {
        //corresponds to path "/json"
    }       
}
Aniket Thakur
la source
vous êtes sûr que cela fonctionnerait? Votre propre réponse ici suggère que le contrôleur ne sera pas touché si {type} est absent du chemin. Je pense que PathVariable Map serait une meilleure approche, ou utiliser des contrôleurs séparés.
Anshul Tiwari
4
Oui, si vous avez juste "/json/{type}"et que le type n'est pas présent, il ne sera pas touché (comme le suggère ma réponse liée), mais ici vous l'avez value = {"/json/{type}", "/json" }. Donc, si l'un d'entre eux correspond à la méthode du contrôleur, il sera touché.
Aniket Thakur
Est-il possible que le même type de valeur, etc., soit RequestParam et RequestParam aussi?
zygimantus
Cela fonctionne, mais de toute façon la fonction du contrôleur ne sera pas appelée car il attend un paramètre là
EliuX
15
Cela fonctionne, et depuis Spring 4.3.3, vous pouvez également aller avec @PathVariable(required = false)et obtenir null si la variable n'est pas présente.
Nicolai Ehemann
76

Il n'est pas bien connu que vous pouvez également injecter une carte des variables de chemin en utilisant l'annotation @PathVariable. Je ne sais pas si cette fonctionnalité est disponible dans Spring 3.0 ou si elle a été ajoutée plus tard, mais voici une autre façon de résoudre l'exemple:

@RequestMapping(value={ "/json/{type}", "/json" }, method=RequestMethod.GET)
public @ResponseBody TestBean typedTestBean(
    @PathVariable Map<String, String> pathVariables,
    @RequestParam("track") String track) {

    if (pathVariables.containsKey("type")) {
        return new TestBean(pathVariables.get("type"));
    } else {
        return new TestBean();
    }
}
Paul Wardrip
la source
1
Je l'utilise tout le temps. Cela est pratique lorsque je veux une seule méthode pour gérer différents types d'URI ex: {"/ json / {type}", "/ json / {type} / {xyz}", "/ json / {type} / {abc} "," / json / {type} / {abc} / {quelque chose} "," / json "}
Vaibs
25

Vous pouvez utiliser un:

@RequestParam(value="somvalue",required=false)

pour les paramètres optionnels plutôt qu'un pathVariable

Maleck13
la source
1
Ceci est spécifique à la version, il semble. No go for Spring 3.
Stu Thompson
5
J'utilise actuellement cette méthode pour un projet Spring 3.1, et la documentation dit que cela fonctionne pour 2.5+, donc cela fonctionne définitivement pour Spring 3. EDIT: source .
Evan B.
22
C'est vrai, mais ce n'est pas le sujet de la question. L'utilisation des paramètres de requête est en effet mentionnée dans la question comme "Une solution de contournement évidente" , mais la question elle-même concerne les paramètres de chemin . Ce n'est pas une solution pour les paramètres de chemin facultatifs.
Arjan
9
PathVariable et RequestParam sont différents.
Timeless
10

Exemples de Spring 5 / Spring Boot 2:

blocage

@GetMapping({"/dto-blocking/{type}", "/dto-blocking"})
public ResponseEntity<Dto> getDtoBlocking(
        @PathVariable(name = "type", required = false) String type) {
    if (StringUtils.isEmpty(type)) {
        type = "default";
    }
    return ResponseEntity.ok().body(dtoBlockingRepo.findByType(type));
}

réactif

@GetMapping({"/dto-reactive/{type}", "/dto-reactive"})
public Mono<ResponseEntity<Dto>> getDtoReactive(
        @PathVariable(name = "type", required = false) String type) {
    if (StringUtils.isEmpty(type)) {
        type = "default";
    }
    return dtoReactiveRepo.findByType(type).map(dto -> ResponseEntity.ok().body(dto));
}
kinjelom
la source
6

Exemple simplifié du commentaire de Nicolai Ehmann et de la réponse de wildloop (fonctionne avec Spring 4.3.3+), vous pouvez essentiellement utiliser required = falsemaintenant:

  @RequestMapping(value = {"/json/{type}", "/json" }, method = RequestMethod.GET)
  public @ResponseBody TestBean testAjax(@PathVariable(required = false) String type) {
    if (type != null) {
      // ...
    }
    return new TestBean();
  }
rogerdpack
la source
-5
$.ajax({
            type : 'GET',
            url : '${pageContext.request.contextPath}/order/lastOrder',
            data : {partyId : partyId, orderId :orderId},
            success : function(data, textStatus, jqXHR) });

@RequestMapping(value = "/lastOrder", method=RequestMethod.GET)
public @ResponseBody OrderBean lastOrderDetail(@RequestParam(value="partyId") Long partyId,@RequestParam(value="orderId",required=false) Long orderId,Model m ) {}
Ankush Mundada
la source
3
Vous voudrez peut-être modifier un texte dans votre réponse, en expliquant pourquoi vous pensez que cela contribue à résoudre le problème en question (4 ans plus tard).
Qirel