【问题标题】:Same rest endpoint with different PathVariable具有不同 PathVariable 的相同休息端点
【发布时间】:2017-11-13 23:05:47
【问题描述】:

我正在尝试创建两个具有相同 uri 但类型不同的休息端点。 第一个将按 EAN (Int) 搜索,第二个将按 id (String) 搜索。我可以以某种方式重载端点吗?我在 Kotlin 中使用 Spring Boot

@GetMapping("/book/{ean}")
fun getABookByEan(@PathVariable ean: Int) : ResponseEntity<*> {
    repository.getByEan(ean)?.let {
        return ResponseEntity.status(HttpStatus.OK).body(it)
    }
    throw ItemNotFoundException()
}

@GetMapping("/book/{id}")
fun getABookById(@PathVariable id: String) : ResponseEntity<*> {
    repository.getById(id)?.let {
        return ResponseEntity.status(HttpStatus.OK).body(it)
    }
    throw ItemNotFoundException()
}

在此之后,我遇到了一个异常,即多个方法被映射到同一个端点。

...NestedServletException:请求处理失败;嵌套异常是 java.lang.IllegalStateException: 为 HTTP 路径映射的不明确的处理程序方法...

【问题讨论】:

  • 您应该为 /book/byEan/{ean}/book/byId/{id} 创建两个映射

标签: java spring rest spring-boot kotlin


【解决方案1】:

如何使用矩阵参数?

所以对于id,你可以使用路径参数-/books/{id}

对于 ean,矩阵参数 - /books;ean={ean}

事实上,对于 id 你也可以使用矩阵参数 - /books;{id}/books;id={id}

矩阵参数的通用url - /{resource-name}[;{selector}]/

来源 - Apigee REST API 设计最佳实践

【讨论】:

    【解决方案2】:

    开发一个查询过滤器/查询标准来处理类似的事情是有益的:

    /book?q=ean+eq+abcdefg (meaning ean=abcdefg)
    /book?q=id+gt+1000  (meaning id>1000)
    

    等等。

    【讨论】:

      【解决方案3】:

      我发现如果我想坚持使用我的 API,唯一的方法就是使用正则表达式。

      @GetMapping("/book/{ean:[\\d]+}")
      
      @GetMapping("/book/{id:^[0-9a-fA-F]{24}$}")
      

      有了它,MongoDB 生成的 16 进制 24 字符可以与简单的数字区分开来。如果有人找到更好的方法,请在 cmets 中告诉我。

      【讨论】:

        【解决方案4】:

        在映射级别上是不可能的。可能您应该尝试以下路径:

        /book/ean/{ean}
        /book/id/{id}
        

        或者只是

        /book/id/{someUniversalId}
        

        然后区分可执行代码中不同类型的 id。

        【讨论】:

        • 这不是问题的上下文。不同的网址应该可以工作。问题是具有不同路径变量的相同端点。
        【解决方案5】:

        从 HTTP 的角度来看,它是同一个端点,因为它是基于文本的协议,并且路径参数始终是一个字符串。因此,Spring 抛出异常。

        要处理这个问题,您可以在方法体中识别参数类型:

        @GetMapping("/book/{identifier}")
        fun getABookById(@PathVariable identifier: String) : ResponseEntity<*> {
            try {
                val id = identifier.toInt()
                // id case
                repository.getById(id)?.let {
                    return ResponseEntity.status(HttpStatus.OK).body(it)
                }
            } catch (e: NumberFormatException) {
                // ean case
                repository.getByEan(identifier)?.let {
                    return ResponseEntity.status(HttpStatus.OK).body(it)
                }
            }
            throw ItemNotFoundException()
        }
        

        或将 ean 或 id 作为 @RequestParam 传递,例如 /book?ean=abcdefg, /book?id=5。

        【讨论】:

        • 对流使用 try/catch 是个坏主意。已通过接受的答案支持此用例。
        猜你喜欢
        • 2018-11-02
        • 1970-01-01
        • 2016-05-01
        • 1970-01-01
        • 2020-12-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多