Com o Spring, posso criar uma variável de caminho opcional?

187

Com o Spring 3.0, posso ter uma variável de caminho opcional?

Por exemplo

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

Aqui eu gostaria /json/abcou /jsonchame o mesmo método.
Uma solução óbvia é declarada typecomo um parâmetro de solicitação:

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

e então /json?type=abc&track=aaou /json?track=rrvai funcionar

Shamik
fonte

Respostas:

194

Você não pode ter variáveis ​​de caminho opcionais, mas pode ter dois métodos de controlador que chamam o mesmo código de serviço:

@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
fonte
5
@ Shamik: Esta é uma razão convincente para não usar variáveis ​​de caminho, na minha opinião. A proliferação combinatória pode sair rapidamente do controle.
5118 skaffman
9
Na verdade, não porque o caminho não pode ser tão complexo enquanto está sendo preenchido com componentes opcionais. Se você tiver mais de um ou no máximo dois elementos de caminho opcionais, considere seriamente mudar alguns deles para solicitar parâmetros.
22312 Patrick Cornelissen
1
E para algumas pessoas, tendo a segunda chamada método de controlador o primeiro método de controlador pode funcionar tão bem, se, por exemplo, o parâmetro diferentes podem ser fornecidas por outros meios
chrismarx
3
Por favor, considere atualizar sua resposta, em vez de criar dois métodos de controlador na versão mais recente do Spring, podemos usar apenas @RequestMappingdois valores, como em: stackoverflow.com/questions/17821731/…
csharpfolk
omg, como você espera manter esses pontos de extremidade? E se, em vez de apenas uma variável de caminho, tivermos 5, faça as contas para mim, quantos pontos de extremidade você faria? Por favor, faça-me um favor e substituir @PathVariablea@RequestParam
Guilherme Alencar
114

Se você estiver usando Spring 4.1 e Java 8 você pode usar java.util.Optionalque é apoiado em @RequestParam, @PathVariable, @RequestHeadere @MatrixVariableno 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
fonte
Você tem certeza que isso funcionaria? Sua própria resposta aqui sugere que o controlador não será atingido se {type} estiver ausente no caminho. Eu acho que o PathVariable Map seria uma abordagem melhor, ou usando controladores separados.
Anshul Tiwari
4
Sim, se você apenas tiver "/json/{type}"e digitar não estiver presente, não será atingido (como minha resposta vinculada sugere), mas aqui está value = {"/json/{type}", "/json" }. Portanto, se alguém corresponder ao método do controlador, será atingido.
Aniket Thakur
É possível que o mesmo valor, etc., seja também RequestParam e RequestParam?
Zygimantus
Funciona, mas de qualquer maneira a função do controlador não será chamada porque espera um parâmetro lá #
3030 EliuX
15
Isso funciona e, desde o Spring 4.3.3, você também pode ir @PathVariable(required = false)e obter null se a variável não estiver presente.
Nicolai Ehemann 5/17
76

Não é sabido que você também pode injetar um mapa das variáveis ​​de caminho usando a anotação @PathVariable. Não tenho certeza se esse recurso está disponível no Spring 3.0 ou se foi adicionado posteriormente, mas aqui está outra maneira de resolver o exemplo:

@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
fonte
1
Eu o uso o tempo todo. Isso é útil quando eu quero um único método para lidar com diferentes tipos de uri, por exemplo: {"/ json / {type}", "/ json / {type} / {xyz}", "/ json / {type} / {abc}", "/ json / {type} / {abc} / {} algo", "/ json"}
Vaibs
25

Você pode usar um:

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

para parâmetros opcionais em vez de um pathVariable

Maleck13
fonte
1
Parece que é uma versão específica. Não vá para a Primavera 3.
Stu Thompson
5
Atualmente, esse método é usado para um projeto do Spring 3.1, e os documentos dizem que ele funciona para 2.5+, portanto, definitivamente funciona para o Spring 3. EDIT: source .
Evan B.
22
É verdade, mas não é disso que se trata. O uso de parâmetros de solicitação é realmente mencionado na pergunta como "Uma solução alternativa óbvia" , mas a pergunta em si é sobre parâmetros de caminho . Esta não é uma solução para parâmetros de caminho opcionais.
Arjan #
9
PathVariable e RequestParam são diferentes.
Timeless
10

Exemplos Spring 5 / Spring Boot 2:

bloqueio

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

reativo

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

Exemplo simplificado do comentário de Nicolai Ehmann e da resposta do wildloop (funciona com o Spring 4.3.3+), basicamente você pode usar required = falseagora:

  @RequestMapping(value = {"/json/{type}", "/json" }, method = RequestMethod.GET)
  public @ResponseBody TestBean testAjax(@PathVariable(required = false) String type) {
    if (type != null) {
      // ...
    }
    return new TestBean();
  }
rogerdpack
fonte
-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
fonte
3
Você pode editar algum texto na sua resposta, explicando por que você acha que isso contribui para a solução do problema em questão (quatro anos depois).
`` Qirel