【问题标题】:Optional does not throw exception when the return value is nullOptional 在返回值为 null 时不抛出异常
【发布时间】:2019-07-21 20:06:02
【问题描述】:

我正在学习 Spring Boot,当服务在数据库上找不到项目时,我试图抛出异常,因此,我尝试了 optional,但是当我测试它时,除了异常之外,我只得到一个空响应

    @GetMapping(value = "/compras", produces = "application/json")
public Optional<Compras> retrieveAllCompras(@RequestParam String id) {
    return Optional.of(compraRepository.findById(id)).orElseThrow(RuntimeException::new);

我希望在数据库中找不到该项目时出现异常

【问题讨论】:

    标签: java spring-boot java-8 response optional


    【解决方案1】:

    Optional.of 期望纯值。您也可以在文档中找到信息,

    /**
     * Constructs an instance with the described value.
     *
     * @param value the non-{@code null} value to describe
     * @throws NullPointerException if value is {@code null}
     */
    private Optional(T value) {
        this.value = Objects.requireNonNull(value);
    }
    

    例子,

    jshell> Optional.of(100)
    $2 ==> Optional[100]
    
    jshell> Optional.of(null)
    |  Exception java.lang.NullPointerException
    |        at Objects.requireNonNull (Objects.java:221)
    |        at Optional.<init> (Optional.java:107)
    |        at Optional.of (Optional.java:120)
    |        at (#1:1)
    

    如果你的值在运行时可以是null,你可以使用.ofNullable

    jshell> Optional.ofNullable(null)
    $3 ==> Optional.empty
    

    函数式编程的思想是为所有输入返回一个值,而不是抛出Exception,这会破坏函数组合。

    jshell> Function<Integer, Optional<Integer>> f = x -> Optional.of(x + 1)
    f ==> $Lambda$23/0x0000000801171c40@6996db8
    
    jshell> Function<Integer, Optional<Integer>> g = x -> Optional.of(x * 2)
    g ==> $Lambda$24/0x0000000801172840@7fbe847c
    
    jshell> f.apply(5).flatMap(x -> g.apply(x))
    $13 ==> Optional[12]
    

    因此,在您的示例中,您可以将 Optional.empty() 视为未找到项目,但 Spring 也会将其视为 200,这仍然比抛出 500 更好。您可能想要发送404 准确。

    @GetMapping(
      value = "/compras", 
      produces = "application/json"
    )
    public Optional<Compras> retrieveAllCompras(@RequestParam String id) {
        return Optional.ofNullable(compraRepository.findById(id)); //will response as 200 even when no item found
    }
    

    您可以使用ResponseEntity&lt;A&gt; to set specific http status

    回复404 的传统方式是defining specific exception

    import org.springframework.web.server.ResponseStatusException;
    import org.springframework.http.HttpStatus;
    
    @GetMapping(
      value = "/compras", 
      produces = "application/json"
    )
    public Compras retrieveAllCompras(@RequestParam String id) {
        return Optional.ofNullable(compraRepository.findById(id))
                       .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "item not found"))
    }
    

    【讨论】:

    • 非常好,我应该添加 optional.empty,如果是这样,通过告诉 item not found 来返回新的响应,对吗?非常感谢!
    • 可以,你可以设置http响应码为404。 Spring 默认情况下,如果一切顺利,则让您发回200;如果您抛出通用异常,则让您发回500。但如您所知,500 代表服务器错误。在您的示例中,如果找不到项目,理想情况下是404
    • @Santiagomolanoperdomo 我添加了一个示例,当Optional.empty 时将发送404
    • 非常感谢!,我不知道 ResponeStatusException,我通过创建一个新类来做到这一点,非常感谢!
    • 这确实是一个优秀答案的定义!加一个!
    【解决方案2】:

    想想这里的类型,如果你想返回Optional,你会做一个.orElse,你必须重新包装它。这将是一种方法:

     @GetMapping(value = "/compras", produces = "application/json")
    public Optional<Compras> retrieveAllCompras(@RequestParam String id) {
        return Optional.ofNullable(compraRepository.findById(id)).
                         map(Optional::of).
                         orElseThrow(() -> new RuntimeException("Not found"));
    

    我会解释:

    1. Optional.ofNullable() 将返回一个Optional&lt;Compras&gt;,其值为Optional("some compras")Optional.Empty
    2. map(Optional::of) 将再次包装可选项,因此您将拥有 Optional[Optional&lt;Compras&gt;] 或(这里的技巧)Optional.Empty,因为 map 不会包装 Optional.Empty(这是他加入我们的原因)
    3. 最后,orElseThrow(() -&gt; new RuntimeException("Not found")) 将解压您的可选项,在这种情况下从Optional[Optional&lt;Compras&gt;]Optional&lt;Compras&gt;,或者如果是Optional.Empty,它将抛出异常

    【讨论】:

      【解决方案3】:

      你的控制器返回Optional&lt;Compras&gt;。这意味着不会调用#get 方法。如果 Optional 值,您的控制器将返回 null。

      更改为:public Compras retrieveAllCompras...,您将获得例外

      【讨论】:

        【解决方案4】:

        从对象名称和调用方法(compraRepository.findById(id))来看,您的服务实际上是一个存储库。如果它是 Spring Data 存储库,那么您应该将 Optional 的创建留给 Spring Data:Null Handling of Repository Methods。所以你会有

        compraRepository.findById(id).orElseThrow(NotFoundException::new)
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-12-22
          • 2012-06-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-03-10
          • 1970-01-01
          • 2022-07-22
          相关资源
          最近更新 更多