简体   繁体   中英

Rest controller - Tyr to reduce the number of endpoint with parameters but get Ambiguous mapping error

I want to implement some simple endpoint in spring, trying to be as much Restful as possible and reduce the number of URL to use. Here are the GET url I want to call: (this is a simplified version)

GET /users
GET /users?id=123
GET /users?username=xyz

I used this controller:

@GetMapping()
public @ResponseBody
OutputUserDTO getUserByParameter(@RequestParam(required = false) String id,
                                 @RequestParam(required = false) String username) {
    if (id != null && !id.isEmpty()) {
        return userService.getUserById(id);
    }
    if (username != null && !username.isEmpty()) {
        return userService.getUserByUsername(username);
    }

    throw new MissingParameterException("...some message...");
}

@GetMapping()
public @ResponseBody
List<OutputUserDTO> getUsers() {
    return userService.getUsers();
}

Of course I get an error, that is Ambiguous mapping .

I thought to always return a List so that I can merge the 2 endpoints and, in case you pass some parameters, return a Singleton... even though I don't know if it's a correct practice. Or else, create one endpoint for each parameter, GET /users/{userId}, GET /users/{username}, ... but I don't like it neither (If I have 10 different way to get a user then I'll have to implement 10 endpoints:S)

What are some good practices in this case?? Thanks.

Replace MissingParameterException with return userService.getUsers(); , and get rid of the other method, you know, the one with exactly the same mapping as the first method.

To make that work, you'd have to change return type to Object, which is not going to be a problem, since it's the actual object returned that controls the effect of @ResponseBody , not the declared type.

@GetMapping()
@ResponseBody
public Object getUserByParameter(@RequestParam(required = false) String id,
                                 @RequestParam(required = false) String username) {
    if (id != null && ! id.isEmpty()) {
        return userService.getUserById(id);
    }
    if (username != null && ! username.isEmpty()) {
        return userService.getUserByUsername(username);
    }
    return userService.getUsers();
}

FYI: @ResponseBody is a method-level annotation, so it should be listed before any keyword modifiers.

The Java Language Specification, section 8.3.1. Field Modifiers , says:

FieldModifier:
(one of)
Annotation public protected private
static final transient volatile

[...]

If two or more (distinct) field modifiers appear in a field declaration, it is customary, though not required, that they appear in the order consistent with that shown above in the production for FieldModifier.

It should be like @GetMapping("/users") on respective method

http://www.appsdeveloperblog.com/pathvariable-spring-mvc/

I suppose that the reason for that is, in getUserByParameter, both parameters are optional but if both the parameters are not passed it will conflict with your second getMapping.

more over, what is returned changes in the three scenarios. scenario 1 returns a list of DTOs while scenarios 2 & 3 return a single DTO

i dont think you can handle all three scenarios using your request path /users unless you want to wrap even a single DTO in a list, in which case you can simply merge your two methods. call getUsers() when both parameters are missing, in other cases, do what you currently do but wrap the response in a list.

if you want to keep them separate and return DTO or List, you should probably separate them out into /users and /user by specifying @GetMapping("/user") on method one and @GetMapping("/users") on method two

hope this helps

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM