java—在同一个控制器中具有getall、getbyid和getbyemail的控制器方法,同时遵循RESTfulAPI标准

bcs8qyzn  于 2021-07-06  发布在  Java
关注(0)|答案(2)|浏览(357)

我有一个实体 Users ,其中包含 id 以及 emailAddress . 我想写一个同时有3个get方法的控制器:
getall()- http://localhost:8080/api/users?pageNumber=0&pageSize=10 获取(uuid)- http://localhost:8080/api/users/209347293875928375928 获取(字符串电子邮件)- http://localhost:8080/api/users/fireball%40email.com 或者 http://localhost:8080/api/users?emailAddress="fireball%40email.com" 注意:这两段代码的区别在于最后一个函数的参数(第一个是 @PathVariable 第二个是 @RequestParam .
我尝试了两种方法来实现这一点(都会遇到一个单独的问题),第一种是:

@GetMapping
  @ApiOperation(value = "List all Users")
  @ApiResponses({
    @ApiResponse(code = 200, message = "OK", response = User.class, responseContainer = "List"),
    @ApiResponse(code = 401, message = "Unauthorized", response = Error.class),
    @ApiResponse(code = 404, message = "Not Found", response = Error.class),
    @ApiResponse(code = 500, message = "Internal Server Error", response = Error.class)
  })
  @Transactional(readOnly = true)
  public ResponseEntity<Page<User>> getAll(
      @RequestParam(required = false, defaultValue = "0") int pageNumber,
      @RequestParam(required = false, defaultValue = "10") int pageSize) {
    return ResponseEntity.ok(UserService.getUsers(pageNumber, pageSize));
  }

  @GetMapping(path = "/{id}")
  @ApiOperation(value = "Get User by ID")
  @Transactional(readOnly = true)
  @ApiResponses({
    @ApiResponse(code = 200, message = "OK", response = User.class),
    @ApiResponse(code = 401, message = "Unauthorized", response = Error.class),
    @ApiResponse(code = 404, message = "Not Found", response = Error.class),
    @ApiResponse(code = 500, message = "Internal Server Error", response = Error.class)
  })
  public ResponseEntity<?> get(@PathVariable("id") final UUID id) {

    return UserService.get(id)
        .map(ResponseEntity::ok)
        .map(ResponseEntity.class::cast)
        .orElse(
            ResponseEntity.status(NOT_FOUND).body(new Error(format(USER_NOT_FOUND_MESSAGE, id))));
  }

  @GetMapping
  @ApiOperation(value = "Get User by Email Address")
  @Transactional(readOnly = true)
  @ApiResponses({
    @ApiResponse(code = 200, message = "OK", response = User.class),
    @ApiResponse(code = 401, message = "Unauthorized", response = Error.class),
    @ApiResponse(code = 404, message = "Not Found", response = Error.class),
    @ApiResponse(code = 500, message = "Internal Server Error", response = Error.class)
  })
  public ResponseEntity<?> get(@RequestParam("email") final String email) {

    return UserService.get(email)
        .map(ResponseEntity::ok)
        .map(ResponseEntity.class::cast)
        .orElse(
            ResponseEntity.status(NOT_FOUND).body(new Error(format(USER_NOT_FOUND_MESSAGE, email))));
  }

上述方法在编译时失败 Ambiguous mapping. Cannot map 'userController' method 作为错误。基本上 getAll() 以及 get(@RequestParam("email") final String email) 具有相同的url路径- /users .
第二是:

@GetMapping
  @ApiOperation(value = "List all Users")
  @ApiResponses({
    @ApiResponse(code = 200, message = "OK", response = User.class, responseContainer = "List"),
    @ApiResponse(code = 401, message = "Unauthorized", response = Error.class),
    @ApiResponse(code = 404, message = "Not Found", response = Error.class),
    @ApiResponse(code = 500, message = "Internal Server Error", response = Error.class)
  })
  @Transactional(readOnly = true)
  public ResponseEntity<Page<User>> getAll(
      @RequestParam(required = false, defaultValue = "0") int pageNumber,
      @RequestParam(required = false, defaultValue = "10") int pageSize) {
    return ResponseEntity.ok(UserService.getUsers(pageNumber, pageSize));
  }

  @GetMapping(path = "/{id}")
  @ApiOperation(value = "Get User by ID")
  @Transactional(readOnly = true)
  @ApiResponses({
    @ApiResponse(code = 200, message = "OK", response = User.class),
    @ApiResponse(code = 401, message = "Unauthorized", response = Error.class),
    @ApiResponse(code = 404, message = "Not Found", response = Error.class),
    @ApiResponse(code = 500, message = "Internal Server Error", response = Error.class)
  })
  public ResponseEntity<?> get(@PathVariable("id") final UUID id) {

    return UserService.get(id)
        .map(ResponseEntity::ok)
        .map(ResponseEntity.class::cast)
        .orElse(
            ResponseEntity.status(NOT_FOUND).body(new Error(format(USER_NOT_FOUND_MESSAGE, id))));
  }

  @GetMapping(path = "/{email}")
  @ApiOperation(value = "Get User by Email Address")
  @Transactional(readOnly = true)
  @ApiResponses({
    @ApiResponse(code = 200, message = "OK", response = User.class),
    @ApiResponse(code = 401, message = "Unauthorized", response = Error.class),
    @ApiResponse(code = 404, message = "Not Found", response = Error.class),
    @ApiResponse(code = 500, message = "Internal Server Error", response = Error.class)
  })
  public ResponseEntity<?> get(@PathVariable("email") final String email) {

    return UserService.get(email)
        .map(ResponseEntity::ok)
        .map(ResponseEntity.class::cast)
        .orElse(
            ResponseEntity.status(NOT_FOUND).body(new Error(format(USER_NOT_FOUND_MESSAGE, email))));
  }

在这里我遇到了控制器无法解决的问题 get(@PathVariable("email") final String email 以及 get(@PathVariable("id") final UUID id) 出现以下错误:

Ambiguous handler methods mapped for '/api/gems/users/fireball%40email.com': {public org.springframework.http.ResponseEntity com.personal.project.controllers.UserController.get(java.util.UUID), public org.springframework.http.ResponseEntity com.personal.project.controllers.UserController.get(java.lang.String)}
to94eoyn

to94eoyn1#

您可以通过将这两条不明确的路径合并为一条来解决它。对于第一种情况:

@GetMapping
  @ApiOperation(value = "List all Users")
  @ApiResponses({
    @ApiResponse(code = 200, message = "OK", response = User.class, responseContainer = "List"),
    @ApiResponse(code = 401, message = "Unauthorized", response = Error.class),
    @ApiResponse(code = 404, message = "Not Found", response = Error.class),
    @ApiResponse(code = 500, message = "Internal Server Error", response = Error.class)
  })
  @Transactional(readOnly = true)
  public ResponseEntity<?> getUsers(
      @RequestParam(required = false, defaultValue = "0") int pageNumber,
      @RequestParam(required = false, defaultValue = "10") int pageSize,
// add email as param.
      @RequestParam(required = false) String email,

) {
    if(email ==null || StringUtils.isEmpty(email)){
       return ResponseEntity.ok(UserService.getUsers(pageNumber, pageSize));
    }else return UserService.get(email)
        .map(ResponseEntity::ok)
        .map(ResponseEntity.class::cast)
        .orElse(
            ResponseEntity.status(NOT_FOUND).body(new Error(format(USER_NOT_FOUND_MESSAGE, email))));
  }

对于第二个:

// change the type UUID id to string.
  @GetMapping(path = "/{id}")
  ...
  public ResponseEntity<?> get(@PathVariable("id") final String id) {
    // check if id as an uuid or email, and based on that take action
  }
aij0ehis

aij0ehis2#

与我之前的回答一致,您只需要两个控制器方法。然后,在处理 GET /users 请求,是否存在 name query参数将确定服务器是获取所有用户还是通过电子邮件过滤用户。
请参见下面的示例。为了简化,我没有添加openapi注解,也没有删除分页,但您可以根据需要轻松添加它们:

@RestController
@RequestMapping(path = "/users", 
                produces = MediaType.APPLICATION_JSON_VALUE)
public class UserController {

    @GetMapping
    public ResponseEntity<List<User>> findUsers(@RequestParam("email") final String email) {
        // If email is null/empty/blank, then fetch all users
        // Otherwise filter users by email
    }

    @GetMapping(path = "/{id}")
    public ResponseEntity<User> findUserById(@PathVariable("id") final UUID id) {
        // Find user with the given ID
    }
}

如果您计划支持多个过滤器(或者只是想避免 if -'else's或自定义存储库方法),使用示例查询(qbe)可能是个好主意,它由spring数据支持:
示例查询(qbe)是一种界面简单、用户友好的查询技术。它允许动态查询创建,并且不需要编写包含字段名的查询。实际上,示例查询根本不要求您使用特定于存储区的查询语言编写查询。
文件还说明了以下内容:
“按示例查询”api由三部分组成:
探测:包含填充字段的域对象的实际示例。 ExampleMatcher :的 ExampleMatcher 包含有关如何匹配特定字段的详细信息。它可以跨多个应用程序重用 Example s。 Example :安 Example 由探头和 ExampleMatcher . 它用于创建查询。
通过示例查询非常适合几种用例:
使用一组静态或动态约束查询数据存储。
频繁重构域对象而不必担心破坏现有查询。
独立于底层数据存储api工作。
通过示例进行查询也有几个限制:
不支持嵌套或分组的属性约束,例如 firstname = ?0 or (firstname = ?1 and lastname = ?2) .
仅支持字符串的start/contains/ends/regex匹配和其他属性类型的精确匹配。
请参见下面如何在您的案例中使用示例查询:

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserFilter {
    private String email;
}
@RestController
@RequiredArgsConstructor
@RequestMapping(path = "/users", 
                produces = MediaType.APPLICATION_JSON_VALUE)
public class UserController {

    private final UserService userService;

    @GetMapping
    public ResponseEntity<List<User>> findUsers(@RequestParam("email") final String email) {
        UserFilter filter = UserFilter.builder().name(email).build();
        List<User> users = userService.findUsers(filter);
        return ResponseEntity.ok(users);
    }

    @GetMapping(path = "/{id}")
    public ResponseEntity<User> findUserById(@PathVariable("id") final UUID id) {
        User user = userService.findUserById(filter).orElseThrow(SomeSortOfException::new);
        return ResponseEntity.ok(user); 
    }
}
@Service
@RequiredArgsConstructor
public class UserService {

    private final UserRepository userRepository;

    public List<User> findUsers(UserFilter filter) {

        User user = new User();
        user.setEmail(filter.getEmail());

        Example<User> example = Example.of(user);
        return userRepository.findAll(example);
    }

    public Optional<User> findUserById(UUID id) {
        return userRepository.findById(id);
    }
}

相关问题