【问题标题】:SpringBoot/Kotlin and Versioning through Content Negotiation: correct approach?SpringBoot/Kotlin 和通过内容协商进行版本控制:正确的方法?
【发布时间】:2020-07-10 00:21:41
【问题描述】:

我一直在尝试将内容协商作为我的 SpringBoot/Kotlin 应用程序的后端版本控制。我有以下内容:

 @GetMapping("/user", produces = [MediaType.APPLICATION_JSON_VALUE])
      fun getUsers() {
        //some code here
      }

我发现 this project 结合了“接受”标头和“接受版本”自定义标头。我想知道这是否是实现内容协商方法的正确方法,如果不是,我该如何解决?

@GetMapping("/user", produces = [MediaType.APPLICATION_JSON_VALUE], headers = ["Accept-Version=$CUSTOM_ACCEPT_HEADER"])
          fun getUsers() {
            //some code here
          }

object VersioningUtility {
  const val CUSTOM_ACCEPT_HEADER = "vnd.sample.com-v1+json"
  //here more constants as each controller can be versioned independently
}

谢谢

【问题讨论】:

    标签: spring-boot kotlin api-versioning


    【解决方案1】:

    是的,您可以使用内容协商实现 API 版本控制,方法是使用您指定的自定义标头和标头值。但是,由于这不是标准标头,因此您可能需要自己处理其他情况,例如:

    • 标头不存在时的默认表示
    • 将无效媒体类型值作为标头的一部分传递时的异常情况。

    如果您只使用 json 响应,则内容协商的 JSON API 标准是发送带有值 application/vnd.api+jsonAccept 标头。由于Accept 是标准请求标头,因此首选使用它。如果您需要处理其他类型的响应,您仍然可以继续使用自定义标头。

    您可以如下实现内容协商:

    @RestController
    class UserController {
    
        @GetMapping("/users", headers = ["Accept=${VersioningUtility.VERSION_1_HEADER}"])
        fun getUser(): ResponseEntity<Any> {
            return ResponseEntity(listOf(User("Abraham Lincoln")), HttpStatus.OK)
        }
    
        @GetMapping("/users", headers = ["Accept=${VersioningUtility.VERSION_2_HEADER}"])
        fun getNewUser(): ResponseEntity<Any> {
            return ResponseEntity(listOf(NewUser(Name("Abraham", "Lincoln"))), HttpStatus.OK)
        }
    }
    
    data class User(val name: String)
    data class NewUser(val name: Name)
    data class Name(val firstName: String, val lastName: String)
    
    object VersioningUtility {
        const val VERSION_1_HEADER = "application/vnd.v1+json"
        const val VERSION_2_HEADER = "application/vnd.v2+json"
    }
    

    以上内容使您能够拥有两个版本的 GET /users 端点和 Accept 标头。

    当 curl 请求使用 v1 的 header 值时,响应将根据 version v1

    curl -L -X GET 'http://localhost:8080/users' \
    -H 'Accept: application/vnd.v1+json'
    
    [
        {
            "name": "Abraham Lincoln"
        }
    ]
    

    当 curl 请求使用 v2 的 header 值时,响应将根据 version v2

    curl -L -X GET 'http://localhost:8080/users' \
    -H 'Accept: application/vnd.v2+json'
    
    [
        {
            "name": {
                "firstName": "Abraham",
                "lastName": "Lincoln"
            }
        }
    ]
    

    当发送一个无效的标头值时,它会以 406 Not Acceptable

    响应
    curl -L -X GET 'http://localhost:8080/users' \
    -H 'Accept: application/vnd.abc+json'
    
    {
        "timestamp": "2020-04-01T18:33:16.393+0000",
        "status": 406,
        "error": "Not Acceptable",
        "message": "Could not find acceptable representation",
        "path": "/users"
    }
    

    当没有发送Accept header 时,它会以默认版本响应,即 v1 这里

    curl -L -X GET 'http://localhost:8080/users'
    
    [
        {
            "name": "Abraham Lincoln"
        }
    ]
    

    甚至 GitHub 也以类似的方式通过内容协商实现了版本控制,您可以在他们的documentation 中查看。

    【讨论】:

      猜你喜欢
      • 2020-11-18
      • 2016-03-12
      • 1970-01-01
      • 2016-01-05
      • 2013-12-13
      • 1970-01-01
      • 1970-01-01
      • 2012-05-07
      • 2015-06-29
      相关资源
      最近更新 更多