¿Con Spring puedo hacer una ruta opcional variable?

187

Con Spring 3.0, ¿puedo tener una variable de ruta opcional?

Por ejemplo

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

Aquí me gustaría /json/abco /jsonllamar al mismo método.
Una solución obvia declarar typecomo un parámetro de solicitud:

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

y luego /json?type=abc&track=aao /json?track=rrfuncionará

Shamik
fuente

Respuestas:

194

No puede tener variables de ruta opcionales, pero puede tener dos métodos de controlador que invocan el mismo código de servicio:

@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
fuente
55
@Shamik: Esta es una razón convincente para no usar variables de ruta, en mi opinión. La proliferación combinatoria puede salirse rápidamente de control.
skaffman
9
En realidad no porque la ruta no puede ser tan compleja mientras se llena con componentes opcionales. Si tiene más de uno o dos elementos de ruta opcionales, debería considerar seriamente cambiar algunos de ellos para solicitar parámetros.
Patrick Cornelissen el
1
Y para algunas personas, hacer que el segundo método de controlador llame al primer método de controlador también puede funcionar, si, por ejemplo, el parámetro diferente se puede proporcionar por algún otro medio
chrismarx
3
Considere actualizar su respuesta, en lugar de crear dos métodos de controlador en la versión más nueva de Spring, podemos usar @RequestMappingdos valores como en: stackoverflow.com/questions/17821731/…
csharpfolk
omg, ¿cómo espera mantener estos puntos finales? Y si en lugar de solo una variable de ruta tenemos 5, haz los cálculos para mí, ¿cuántos puntos finales harías? Por favor, hazme un favor y reemplazar @PathVariablea@RequestParam
Guilherme Alencar
114

Si está utilizando Spring 4.1 y Java 8, puede usar java.util.Optional que se apoya en @RequestParam, @PathVariable, @RequestHeadery @MatrixVariableen la primavera de 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
fuente
¿Estás seguro de que esto funcionaría? Su propia respuesta aquí sugiere que el controlador no se verá afectado si {type} está ausente de la ruta. Creo que PathVariable Map sería un mejor enfoque, o usar controladores separados.
Anshul Tiwari
44
Sí, si solo tiene "/json/{type}"y el tipo no está presente, no se golpeará (como sugiere mi respuesta vinculada), pero aquí lo tiene value = {"/json/{type}", "/json" }. Entonces, si alguno coincide con el método del controlador, se verá afectado.
Aniket Thakur
¿Es posible que el mismo valor, etc., tipo sea RequestParam y RequestParam también?
zygimantus
Funciona, pero de todos modos no se llamará a la función del controlador porque espera un parámetro allí
EliuX
15
Esto funciona, y desde Spring 4.3.3, también puede ir @PathVariable(required = false)y obtener un valor nulo si la variable no está presente.
Nicolai Ehemann
76

No se sabe bien que también puede inyectar un Mapa de las variables de ruta utilizando la anotación @PathVariable. No estoy seguro de si esta función está disponible en Spring 3.0 o si se agregó más tarde, pero aquí hay otra forma de resolver el ejemplo:

@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
fuente
1
Lo uso todo el tiempo. Esto es útil cuando quiero un solo método para manejar diferentes tipos de uri, por ejemplo: {"/ json / {type}", "/ json / {type} / {xyz}", "/ json / {type} / {abc} "," / json / {type} / {abc} / {something} "," / json "}
Vaibs
25

Podrías usar un:

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

para parámetros opcionales en lugar de una ruta

Maleck13
fuente
1
Esto es específico de la versión, parece. No ir para la primavera 3.
Stu Thompson
55
Actualmente utilizo este método para un proyecto Spring 3.1, y los documentos dicen que funciona para 2.5+, por lo que definitivamente funciona para Spring 3. EDITAR: fuente .
Evan B.
22
Es cierto, pero de esto no se trata la pregunta. El uso de parámetros de solicitud se menciona en la pregunta como "Una solución obvia" , pero la pregunta en sí misma es sobre los parámetros de ruta . Esta no es una solución para parámetros de ruta opcionales.
Arjan
9
PathVariable y RequestParam son diferentes.
Atemporal
10

Ejemplos de Spring 5 / Spring Boot 2:

bloqueo

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

reactivo

@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
fuente
6

Ejemplo simplificado del comentario de Nicolai Ehmann y la respuesta de wildloop (funciona con Spring 4.3.3+), básicamente puede usar required = falseahora:

  @RequestMapping(value = {"/json/{type}", "/json" }, method = RequestMethod.GET)
  public @ResponseBody TestBean testAjax(@PathVariable(required = false) String type) {
    if (type != null) {
      // ...
    }
    return new TestBean();
  }
rogerdpack
fuente
-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
fuente
3
Es posible que desee editar algún texto en su respuesta, explicando por qué cree que esto contribuye a resolver el problema en cuestión (4 años después).
Qirel