0

It is common scenario to search same resource using same data type as PathVariable (for example String).

If I had resource named Student like:

class Student {
   String name;
   String email;
   ...
}

So, both name and email are Strings.

How would I create 2 endpoints to get information about this student by name or by email in Spring Boot?

I would like to have something like this:

@GetMapping(path="/{name}
public StudentResponse getStudentByName(@PathVariable String name) {...}

and

@GetMapping(path="/{email}
public StudentResponse getStudentByEmail(@PathVariable String email) {...}

, but this leads to ambiguous endpoint since these 2 endpoints receive same parameter types.

How would I create endpoints in such case?

I assume my path would have to be different or to have different data types of its PathVariable to avoid ambiguous endpoint. But given that this scenario is quite common case, what is recommended way to do it?

pixel
  • 9,653
  • 16
  • 82
  • 149

2 Answers2

2

You can't create two endpoints like that (student/{email} and student/{name}) I suggest 3 methods:

1- use tow endpoints student/email/{email} and student/name/{name}

2- use @RequestParam to use to query param with your GET endpoint : student/filter?name=n&email=e

3- use this conception two query params one to determine which field you want, the second one represents the value: Then call GET student/filter?field=name&value=somename

@GetMapping(path="/filter")
public StudentResponse getStudentByFeild(@RequestParam String field, @RequestParam String value)
{
   // TODO: if or switch statement on the field value ("name", "email", ...)
   // Example Student s = ("name".equals(field)) ? stdService.findByName(value) : stdService.findByEmail(value)
   // Or switch(field): case "name": service.getByName(value); break; ...
}

4- use POST with @RequestBody that contains your payload (filter, type, values, name, email ...)

1

You can't overload the path variable (at least not with best practices of RESTful end-points). That is meant for the identifier (id) of the resource. What you are trying to do is kind of a search filter. You have 2 options:

  1. Provide the query as JSON content to a method POST

  2. Add query parameter

    GET: /students?name=John Doe
    GET: /students?email=john.doe@unknown.com

Here, a good discussion when you should use query parameters

If you want to use the 2nd approach, you could do something like this

@GetMapping(value = "/students", params = { "name" })
 public Response getStudentByName(@RequestParam String name) {
   //
 }

@GetMapping(value = "/students", params = { "email" })
public Response getStudentByEmail(@RequestParam String email) {
  //
}
Tintin
  • 2,853
  • 6
  • 42
  • 74
  • "You can't overload the path variable (at least RESTfully)." doesn't sound right because REST doesn't care about what's in your URL. – Evert May 23 '22 at 23:05
  • yeah thats why in parenthesis. Technically, you can encode anything in your paths in the name of URLs and still call it REST, but is that how you want to make your RESTful API? – Tintin May 23 '22 at 23:07
  • Maybe? I've definitely seen good HATEOAS apis that just use UUIDs for all paths. One advantage is that it forces uses to discover URIs instead of manually concatenate them. – Evert May 23 '22 at 23:16
  • @Tintin Thanks but how would I use your suggested #2 ? Do I need to pass both name and value? E.g. `public getStudent(@RequestParam name, @RequestParam value) {}` and then somehow decide how to call service based on name? or? – pixel May 24 '22 at 00:31
  • 1
    @pixel - I have edited my answer to answer your question. – Tintin May 24 '22 at 00:52