With Spring can I make an optional path variable?

SpringRest

Spring Problem Overview


With Spring 3.0, can I have an optional path variable?

For example

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

Here I would like /json/abc or /json to call the same method.
One obvious workaround declare type as a request parameter:

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

and then /json?type=abc&track=aa or /json?track=rr will work

Spring Solutions


Solution 1 - Spring

You can't have optional path variables, but you can have two controller methods which call the same service code:

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

Solution 2 - Spring

If you are using Spring 4.1 and Java 8 you can use java.util.Optional which is supported in @RequestParam, @PathVariable, @RequestHeader and @MatrixVariable in 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"
    }   	
}

Solution 3 - Spring

It's not well known that you can also inject a Map of the path variables using the @PathVariable annotation. I'm not sure if this feature is available in Spring 3.0 or if it was added later, but here is another way to solve the example:

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

Solution 4 - Spring

You could use a :

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

for optional params rather than a pathVariable

Solution 5 - Spring

Spring 5 / Spring Boot 2 examples:

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

Solution 6 - Spring

Simplified example of Nicolai Ehmann's comment and wildloop's answer (works with Spring 4.3.3+), basically you can use required = false now:

  @RequestMapping(value = {"/json/{type}", "/json" }, method = RequestMethod.GET)
  public @ResponseBody TestBean testAjax(@PathVariable(required = false) String type) {
    if (type != null) {
      // ...
    }
    return new TestBean();
  }

Solution 7 - Spring

Check this Spring 3 WebMVC - Optional Path Variables. It shows an article of making an extension to AntPathMatcher to enable optional path variables and might be of help. All credits to Sebastian Herold for posting the article.

Solution 8 - Spring

Here is the answer straight from baeldung's reference page :- https://www.baeldung.com/spring-optional-path-variables

Solution 9 - Spring

thanks Paul Wardrip in my case I use required.

@RequestMapping(value={ "/calificacion-usuario/{idUsuario}/{annio}/{mes}", "/calificacion-usuario/{idUsuario}" }, method=RequestMethod.GET)
public List<Calificacion> getCalificacionByUsuario(@PathVariable String idUsuario
        , @PathVariable(required = false) Integer annio
        , @PathVariable(required = false) Integer mes) throws Exception {
    return repositoryCalificacion.findCalificacionByName(idUsuario, annio, mes);
}

Solution 10 - Spring

$.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 ) {}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionShamikView Question on Stackoverflow
Solution 1 - SpringearldouglasView Answer on Stackoverflow
Solution 2 - SpringAniket ThakurView Answer on Stackoverflow
Solution 3 - SpringPaul WardripView Answer on Stackoverflow
Solution 4 - SpringMaleck13View Answer on Stackoverflow
Solution 5 - SpringkinjelomView Answer on Stackoverflow
Solution 6 - SpringrogerdpackView Answer on Stackoverflow
Solution 7 - SpringUresh KView Answer on Stackoverflow
Solution 8 - Springuser1354825View Answer on Stackoverflow
Solution 9 - SpringDave RinconView Answer on Stackoverflow
Solution 10 - SpringAnkush MundadaView Answer on Stackoverflow