3

It is possible to test if a parameter exists or not into an URL with Spring?

Below, my current code, but maybe that if(status == null) is a bit dirty right?

@GetMapping
public ResponseEntity<?> getAllTasks(@RequestParam(value = "status", required = false) Integer status) {
    if(status == null) {
        return new ResponseEntity<>(this.taskResource.findAll(), HttpStatus.OK);
    }
    return new ResponseEntity<>(this.tacheResource.getTachesByEtat(status), HttpStatus.OK);
}

The method getAllTasks() will be call in different cases:

  • localhost:8080/tasks
  • localhost:8080/tasks/?status=...

That's why I try to find another way to do this test.

Can you help me ?

Thanks.

Royce
  • 1,557
  • 5
  • 19
  • 44

3 Answers3

6

Depending on your Java version, you can use Optional and isPresent() (but you'll have to get the value from your status variable using status.get()):

@GetMapping
public ResponseEntity<?> getAllTasks(@RequestParam("status") Optional<Integer> status) {
    if(!status.isPresent()) {
        return new ResponseEntity<>(this.taskResource.findAll(), HttpStatus.OK);
    }
    return new ResponseEntity<>(this.tacheResource.getTachesByEtat(status.get()), HttpStatus.OK);
}
Ayrton
  • 2,218
  • 1
  • 14
  • 25
2

Look down, at the end of this answer, for the better solution.

The best approach is following the single responsibility pattern.

As your URL could be called using two different ways, why not splitting it, and letting each one implement its own logic?

@GetMapping
public ResponseEntity<?> getAllTasks() {
   return ResponseEntity.ok().body(this.taskResource.findAll());
}

@GetMapping
public ResponseEntity<?> getAllTasksWithStatus(@RequestParam("status") final int status) {
   return ResponseEntity.ok().body(this.tacheResource.getTachesByEtat(status));
}

Spring will handle this beautifully.

Note that when you use a single, value property with an Annotation, you don't need to explicitly specify it.

Edit: being that this can have the effect of producing a mapping collision, an even better alternative to @RequestParam is using @PathVariable for the *WithStatus endpoint.

@GetMapping("/status/{status}")
public ResponseEntity<?> getAllTasksWithStatus(@PathVariable("status") final int status) {
    return ResponseEntity.ok().body(this.tacheResource.getTachesByEtat(status));
}

When dealing with complex, long, URLs, there are two roads to take.
Use @PathVariable(s), such as

/{status}/{date}/{name}

Or go for a POST HTTP request. Here you can customize the body as you prefer. I usually go for POST requests when dealing with complex HTTP calls.


Last edit, hopefully:
you can narrow down mapping by specifying the wanted query params.

@GetMapping
public ResponseEntity<?> getAllTasks() {
   return ResponseEntity.ok().body(this.taskResource.findAll());
}

@GetMapping(params = "status")
public ResponseEntity<?> getAllTasksWithStatus(@RequestParam("status") final int status) {
   return ResponseEntity.ok().body(this.tacheResource.getTachesByEtat(status));
}

No more conflicts ;)

As params is an array, you can specify multiple values with

@GetMapping(params = { "status", "date" })
LppEdd
  • 20,274
  • 11
  • 84
  • 139
  • 1
    How would you handle same URL but optional param in this case, since your 2 controller points to same URL. Don't you think it will create ambiguity? – Arpit Agrawal Feb 13 '19 at 19:31
  • @ArpitAgrawal there is a trade off here. The "optional" query parameter is no more optional, in favor of a cleaner solution, architecturally speaking. – LppEdd Feb 13 '19 at 19:34
  • @LppEdd Indeed, this is the best solution for me. However, I got a an error : "Ambigious mapping". you can find my problem here : https://stackoverflow.com/questions/54676727/getmapping-ambigious-mapping/54676857#54676857 – Royce Feb 13 '19 at 19:37
  • 1
    @N.Lamblin let me fire up a Spring project. – LppEdd Feb 13 '19 at 19:40
  • @N.Lamblin check if the proposed solution is ok for you. If changing your URLs schema is not possible, the solution of Optional is the one to go for. However I think that optional parameters should not be used – LppEdd Feb 13 '19 at 19:49
  • @N.Lamblin I just thought about another interesting one. Let me know what you think about the one above. – LppEdd Feb 13 '19 at 19:51
  • The only problem I see with the solution of @PathVariable is if I need several parameters in the future. For exemple status, date and name. I need to create an url like `/status/{status}/date/{date}/name/{name}`. It is a bit strange no? Thanks for your answer. – Royce Feb 13 '19 at 19:58
  • @N.Lamblin solution found. See my edit. Look how I added the ("/") only to the first endpoint, getAllTasks. You just need to call the endpoint with "/". Do you think it's acceptable? – LppEdd Feb 13 '19 at 20:05
  • @N.Lamblin I also gave my opinion on complex URLs. – LppEdd Feb 13 '19 at 20:10
  • Your last edit is the perfect answer! Now I have to find how to specified several parameters (to understand and learn correctly Spring). Somethink like `@GetMapping(params = "status", params = "date")`. – Royce Feb 13 '19 at 20:24
  • @N.Lamblin btw, thank you for this "challenge". It was fun writing this answer. – LppEdd Feb 13 '19 at 20:27
  • @LppEdd big thanks !!!! Where do you find this information? On documentation? Because I don't found them. – Royce Feb 13 '19 at 20:28
  • @ArpitAgrawal Look the answer :). – Royce Feb 13 '19 at 20:28
  • 1
    @N.Lamblin I found it by reading the RequestMapping documentation https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestMapping.html#params-- – LppEdd Feb 13 '19 at 20:29
  • 1
    @N.Lamblin I also answered to your other question, for reference purposes. It will be visited more times in the future. – LppEdd Feb 13 '19 at 20:30
  • 1
    @N.Lamblin yes, i also saw this here : https://stackoverflow.com/questions/15853035/create-two-method-for-same-url-pattern-with-different-arguments . Yes. Indeed a good way. – Arpit Agrawal Feb 13 '19 at 20:32
1

You can use Optional in requestParam annotation. @RequestParam("status") Optional<String> status After that you can check if status is present: status.ifPresent()

bkubiak
  • 315
  • 2
  • 3
  • 12