Czy za pomocą Spring mogę utworzyć opcjonalną zmienną ścieżki?

187

Czy w Spring 3.0 mogę mieć opcjonalną zmienną ścieżki?

Na przykład

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

Tutaj chciałbym /json/abclub /jsonwywołać tę samą metodę.
Jedno oczywiste obejście deklaruje się typejako parametr żądania:

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

a potem /json?type=abc&track=aalub /json?track=rrbędzie działać

Shamik
źródło

Odpowiedzi:

194

Nie możesz mieć opcjonalnych zmiennych ścieżki, ale możesz mieć dwie metody kontrolera, które wywołują ten sam kod usługi:

@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();
}
Earlouglas
źródło
5
@Shamik: Moim zdaniem jest to ważny powód, aby nie używać zmiennych ścieżek. Kombinatoryczna proliferacja może szybko wymknąć się spod kontroli.
skaffman
9
W rzeczywistości nie dlatego, że ścieżka nie może być tak złożona, gdy jest wypełniona opcjonalnymi komponentami. Jeśli masz więcej niż jeden lub maksymalnie dwa opcjonalne elementy ścieżki, powinieneś poważnie rozważyć zmianę kilku z nich na parametry żądań.
Patrick Cornelissen,
1
A dla niektórych osób, wywołanie metody drugiego kontrolera, może również zadziałać metoda pierwszego kontrolera, jeśli na przykład inny parametr można podać za pomocą innych środków
chrismarx
3
Zastanów się nad zaktualizowaniem swojej odpowiedzi, zamiast tworzyć dwie metody kontrolera w nowszej wersji Spring, której możemy użyć @RequestMappingz dwiema wartościami jak na: stackoverflow.com/questions/17821731/...
csharpfolk
omg, w jaki sposób chcesz utrzymać te punkty końcowe? A jeśli zamiast tylko jednej zmiennej ścieżki mamy 5, wykonaj matematykę dla mnie, ile punktów końcowych byś zrobił? Wyświadcz mi przysługę i zastąp @PathVariabledo@RequestParam
Guilherme Alencar
113

Jeśli używasz wiosny 4.1 i Java 8 można użyć java.util.Optionalktóra jest obsługiwana w @RequestParam, @PathVariable, @RequestHeadera @MatrixVariablew 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
źródło
jesteś pewien, że to zadziała? Twoja własna odpowiedź tutaj sugeruje, że kontroler nie zostanie trafiony, jeśli {type} jest nieobecny na ścieżce. Myślę, że PathVariable Map byłoby lepszym podejściem lub przy użyciu oddzielnych kontrolerów.
Anshul Tiwari,
4
Tak, jeśli po prostu masz "/json/{type}"i nie ma typu, nie zostanie trafiony (jak sugeruje moja łączona odpowiedź), ale tutaj masz value = {"/json/{type}", "/json" }. Więc jeśli którykolwiek pasuje, metoda kontrolera zostanie trafiona.
Aniket Thakur
Czy to możliwe, że taką samą wartością itp. Są również RequestParam i RequestParam?
zygimantus
Działa, ale i tak funkcja kontrolera nie zostanie wywołana, ponieważ oczekuje tam parametru
EliuX
15
Działa to, a od wiosny 4.3.3 możesz także przejść do @PathVariable(required = false)i uzyskać wartość null, jeśli zmienna nie jest obecna.
Nicolai Ehemann
76

Nie wiadomo, czy można wstrzyknąć mapę zmiennych ścieżki za pomocą adnotacji @PathVariable. Nie jestem pewien, czy ta funkcja jest dostępna w Spring 3.0, czy została dodana później, ale oto inny sposób rozwiązania tego przykładu:

@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
źródło
1
Używam go cały czas. Jest to przydatne, gdy chcę, aby jedna metoda obsługiwała różne typy URI, np .: {"/ json / {type}", "/ json / {type} / {xyz}", "/ json / {type} / {abc} "," / json / {type} / {abc} / {something} "," / json "}
Vaibs
25

Możesz użyć:

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

dla opcjonalnych parametrów zamiast pathVariable

Maleck13
źródło
1
Wygląda na to, że jest to wersja specyficzna. Nie idź na wiosnę 3.
Stu Thompson
5
Obecnie używam tej metody w projekcie wiosennym 3.1, a doktorzy twierdzą, że działa ona w wersji 2.5+, więc zdecydowanie działa w przypadku wiosny 3. EDYCJA: źródło .
Evan B.
22
To prawda, ale nie o to chodzi w tym pytaniu. Używanie parametrów żądania jest rzeczywiście wspomniane w pytaniu jako „Jedno oczywiste obejście” , ale samo pytanie dotyczy parametrów ścieżki . To nie jest rozwiązanie dla opcjonalnych parametrów ścieżki.
Arjan
9
PathVariable i RequestParam są różne.
Ponadczasowy
10

Przykłady Spring 5 / Spring Boot 2:

bloking

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

reaktywny

@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
źródło
6

Uproszczony przykład komentarza Nicolai Ehmanna i odpowiedzi wildloopa (działa ze Spring 4.3.3+), w zasadzie możesz required = falseteraz użyć :

  @RequestMapping(value = {"/json/{type}", "/json" }, method = RequestMethod.GET)
  public @ResponseBody TestBean testAjax(@PathVariable(required = false) String type) {
    if (type != null) {
      // ...
    }
    return new TestBean();
  }
rogerdpack
źródło
-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
źródło
3
Być może zechcesz edytować tekst w odpowiedzi, wyjaśniając, dlaczego Twoim zdaniem przyczynia się to do rozwiązania danego problemu (4 lata później).
Qirel