为互斥请求参数设计 API 的更好方法是什么?
What is a better way to design an API for mutually exclusive request parameters?
当请求参数互斥时,在 @Controller 方法上设计 API 的更好方法是什么?
假设有一个 API 来提供与请求参数匹配的用户列表。
代码是:
public ResponseEntity getList(@RequestParam(required = false) Integer userId,
@RequestParam(required = false) User.Type userType,
@RequestParam(required = false) Integer age) {
List<User> userList = null;
if (userId != null) {
//logic
userList = getUserByUserId()
} else if (userType != null) {
//logic
userList = getUserByType()
} else if (age != null) {
//logic
userList = getListByAge()
} else {
userList = getAllWithoutCondition();
}
return ResponseEntity.ok(userList);
}
重点是:
用户不能使用多个请求参数进行查询。请求参数只有一个或没有请求参数有效(请求中只能存在userId
、age
、type
之一)。
我不确定针对这种情况设计 API 的更好方法是什么。你能给我一些建议吗?
我喜欢这些人在评论中建议的方法:
@RequestMapping(value = "...", params = {"!userType", "!userAge"})
public ResponseEntity<List<User>> getListByUserId(@RequestParam Integer userId) { ... }
// similarly, define two more
在您开始管理每个端点的限制之前,它看起来很可靠且可行。它看起来乏味且难以维护。此外,我不确定不带参数的端点会如何反应。它会被其他方法调用或隐藏吗?
我建议不要写限制,而是引入条件——每个端点的要求。它可以是下一个格式的 Map<String, Function<String, List<User>>>
:
<param name> -> <action to get a list>
我还建议您将所有传入的请求参数收集到一个 Map<String, String>
中以按大小验证它。
public class Controller {
private Map<String, Function<String, List<User>>> handlers = new HashMap<>();
{
handlers.put("userId", id -> getUsersById(Integer.valueOf(id)));
handlers.put("userType", type -> getUsersByType(User.Type.valueOf(type)));
handlers.put("userAge", age -> getUsersByAge(Integer.valueOf(age)));
}
@RequestMapping("...")
public ResponseEntity<List<User>> getList(@RequestParam Map<String, String> params) {
if (params.size() > 1) {
return ResponseEntity.unprocessableEntity().build();
}
if (params.size() == 0) {
return ResponseEntity.ok(getAllWithoutCondition());
}
Map.Entry<String, String> paramEntry = params.entrySet().iterator().next();
return ResponseEntity.ok(handlers.get(paramEntry.getKey()).apply(paramEntry.getValue()));
}
private List<User> getAllWithoutCondition() { ... }
private List<User> getUsersById(Integer id) { ... }
private List<User> getUsersByType(User.Type type) { ... }
private List<User> getUsersByAge(Integer age) { ... }
}
当请求参数互斥时,在 @Controller 方法上设计 API 的更好方法是什么?
假设有一个 API 来提供与请求参数匹配的用户列表。
代码是:
public ResponseEntity getList(@RequestParam(required = false) Integer userId,
@RequestParam(required = false) User.Type userType,
@RequestParam(required = false) Integer age) {
List<User> userList = null;
if (userId != null) {
//logic
userList = getUserByUserId()
} else if (userType != null) {
//logic
userList = getUserByType()
} else if (age != null) {
//logic
userList = getListByAge()
} else {
userList = getAllWithoutCondition();
}
return ResponseEntity.ok(userList);
}
重点是:
用户不能使用多个请求参数进行查询。请求参数只有一个或没有请求参数有效(请求中只能存在userId
、age
、type
之一)。
我不确定针对这种情况设计 API 的更好方法是什么。你能给我一些建议吗?
我喜欢这些人在评论中建议的方法:
@RequestMapping(value = "...", params = {"!userType", "!userAge"})
public ResponseEntity<List<User>> getListByUserId(@RequestParam Integer userId) { ... }
// similarly, define two more
在您开始管理每个端点的限制之前,它看起来很可靠且可行。它看起来乏味且难以维护。此外,我不确定不带参数的端点会如何反应。它会被其他方法调用或隐藏吗?
我建议不要写限制,而是引入条件——每个端点的要求。它可以是下一个格式的 Map<String, Function<String, List<User>>>
:
<param name> -> <action to get a list>
我还建议您将所有传入的请求参数收集到一个 Map<String, String>
中以按大小验证它。
public class Controller {
private Map<String, Function<String, List<User>>> handlers = new HashMap<>();
{
handlers.put("userId", id -> getUsersById(Integer.valueOf(id)));
handlers.put("userType", type -> getUsersByType(User.Type.valueOf(type)));
handlers.put("userAge", age -> getUsersByAge(Integer.valueOf(age)));
}
@RequestMapping("...")
public ResponseEntity<List<User>> getList(@RequestParam Map<String, String> params) {
if (params.size() > 1) {
return ResponseEntity.unprocessableEntity().build();
}
if (params.size() == 0) {
return ResponseEntity.ok(getAllWithoutCondition());
}
Map.Entry<String, String> paramEntry = params.entrySet().iterator().next();
return ResponseEntity.ok(handlers.get(paramEntry.getKey()).apply(paramEntry.getValue()));
}
private List<User> getAllWithoutCondition() { ... }
private List<User> getUsersById(Integer id) { ... }
private List<User> getUsersByType(User.Type type) { ... }
private List<User> getUsersByAge(Integer age) { ... }
}