Kann ich mit Spring eine optionale Pfadvariable erstellen?

185

Kann ich mit Spring 3.0 eine optionale Pfadvariable haben?

Beispielsweise

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

Hier möchte /json/abcoder möchte ich /jsondie gleiche Methode aufrufen.
Eine offensichtliche Problemumgehung typeals Anforderungsparameter deklarieren :

@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();
}

und dann /json?type=abc&track=aaoder /json?track=rrwird funktionieren

Shamik
quelle

Antworten:

193

Sie können keine optionalen Pfadvariablen haben, aber Sie können zwei Controller-Methoden haben, die denselben Servicecode aufrufen:

@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
quelle
5
@Shamik: Dies ist meiner Meinung nach ein zwingender Grund, keine Pfadvariablen zu verwenden. Die kombinatorische Proliferation kann schnell außer Kontrolle geraten.
Skaffman
9
Eigentlich nicht, weil der Pfad nicht so komplex sein kann, während er mit optionalen Komponenten gefüllt ist. Wenn Sie mehr als ein oder maximal zwei optionale Pfadelemente haben, sollten Sie ernsthaft in Betracht ziehen, einige davon auf Anforderungsparameter umzustellen.
Patrick Cornelissen
1
Und für einige Leute kann es auch funktionieren, wenn die zweite Controller-Methode die erste Controller-Methode
aufruft
2
Bitte erwägen Sie, Ihre Antwort zu aktualisieren, anstatt zwei Controller-Methoden in einer neueren Version von Spring zu erstellen, verwenden wir möglicherweise nur @RequestMappingzwei Werte wie in: stackoverflow.com/questions/17821731/…
csharpfolk
omg wie erwarten Sie diese Endpunkte zu pflegen? Und wenn wir statt nur einer Pfadvariablen 5 haben, rechnen Sie für mich, wie viele Endpunkte würden Sie tun? Bitte tu mir einen Gefallen und ersetzen @PathVariablezu@RequestParam
Guilherme Alencar
113

Wenn Sie mit Spring 4.1 und Java 8 können Sie verwenden , java.util.Optionaldie in unterstützt wird @RequestParam, @PathVariable, @RequestHeaderund @MatrixVariablein 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
quelle
Bist du sicher, dass das funktionieren würde? Ihre eigene Antwort hier deutet darauf hin, dass der Controller nicht getroffen wird, wenn {type} nicht im Pfad enthalten ist. Ich denke, PathVariable Map wäre ein besserer Ansatz oder die Verwendung separater Controller.
Anshul Tiwari
4
Ja, wenn Sie nur haben "/json/{type}"und der Typ nicht vorhanden ist, wird er nicht getroffen (wie meine verlinkte Antwort andeutet), aber hier haben Sie value = {"/json/{type}", "/json" }. Wenn also eine mit der Controller-Methode übereinstimmt, wird sie getroffen.
Aniket Thakur
Ist es möglich, dass derselbe Wert usw. auch RequestParam und RequestParam ist?
Zygimantus
Es funktioniert, aber die Funktion des Controllers wird trotzdem nicht aufgerufen, da dort ein Parameter erwartet wird
EliuX
15
Dies funktioniert, und seit Spring 4.3.3 können Sie auch @PathVariable(required = false)null abrufen, wenn die Variable nicht vorhanden ist.
Nicolai Ehemann
76

Es ist nicht bekannt, dass Sie auch eine Map der Pfadvariablen mit der Annotation @PathVariable einfügen können. Ich bin nicht sicher, ob diese Funktion in Spring 3.0 verfügbar ist oder später hinzugefügt wurde, aber hier ist eine andere Möglichkeit, das Beispiel zu lösen:

@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
quelle
1
Ich benutze es die ganze Zeit. Dies ist praktisch, wenn ich möchte, dass eine einzelne Methode verschiedene Uri-Typen verarbeitet, z. B.: {"/ Json / {type}", "/ json / {type} / {xyz}", "/ json / {Typ} / {abc} "," / json / {Typ} / {abc} / {etwas} "," / json "}
Vaibs
25

Sie könnten verwenden:

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

für optionale Parameter anstelle einer pathVariable

Maleck13
quelle
1
Dies ist anscheinend versionenspezifisch. No go für Frühling 3.
Stu Thompson
5
Derzeit wird diese Methode für ein Spring 3.1-Projekt verwendet. In den Dokumenten wird angegeben, dass sie für 2.5+ funktioniert. Daher funktioniert sie definitiv für Spring 3. EDIT: source .
Evan B.
22
Stimmt, aber darum geht es in der Frage nicht. Mit Anforderungsparameter ist in der Tat in der Frage , wie erwähnt „Eine offensichtliche Abhilfe“ , aber die Frage selbst ist etwa Pfad Parameter. Dies ist keine Lösung für optionale Pfadparameter.
Arjan
9
PathVariable und RequestParam sind unterschiedlich.
Zeitlos
9

Beispiele für Spring 5 / Spring Boot 2:

Blockierung

@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));
}

reaktiv

@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
quelle
5

Vereinfachtes Beispiel für Nicolai Ehmanns Kommentar und Wildloops Antwort (funktioniert mit Spring 4.3.3+), im Grunde können Sie required = falsejetzt verwenden:

  @RequestMapping(value = {"/json/{type}", "/json" }, method = RequestMethod.GET)
  public @ResponseBody TestBean testAjax(@PathVariable(required = false) String type) {
    if (type != null) {
      // ...
    }
    return new TestBean();
  }
Rogerdpack
quelle
-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
quelle
3
Möglicherweise möchten Sie einen Text in Ihrer Antwort bearbeiten und erklären, warum dies Ihrer Meinung nach zur Lösung des vorliegenden Problems beiträgt (4 Jahre später).
Qirel