SpringBoot / Kotlin и управление версиями через согласование контента: правильный подход? - PullRequest
4 голосов
/ 29 марта 2020

Я экспериментировал с Content Negotiation в качестве серверной версии для моего приложения SpringBoot / Kotlin. У меня есть следующее:

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

Я нашел этот проект , объединяющий заголовок accept "и настраиваемый заголовок" Accept-Version ". Интересно, является ли это правильным способом реализации подход к согласованию контента, и если нет, как я могу это исправить?

@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
}

Спасибо

1 Ответ

4 голосов
/ 01 апреля 2020

Да, вы можете реализовать управление версиями API с помощью согласования содержимого, используя собственный заголовок и значение заголовка, как вы указали. Однако, поскольку это не стандартный заголовок, существуют другие сценарии ios, которые вам, возможно, придется обрабатывать самостоятельно, например:

  • представление по умолчанию, когда заголовок отсутствует
  • исключительная сценарий ios, когда недопустимые значения типа носителя передаются как часть заголовка.

Если вы работаете только с json ответами, JSON API стандарт для согласования контента должен отправлять заголовок Accept со значением application/vnd.api+json. Поскольку Accept является стандартным заголовком запроса, его использование является предпочтительным. В случае, если вам нужно обработать другие типы ответов, вы все еще можете go вперед с пользовательским заголовком.

Вы можете реализовать согласование содержимого, как показано ниже:

@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"
}

Вышеуказанное с позволяет вам иметь 2 версии конечной точки GET /users с заголовком Accept.

Когда запрос скручивания сделан с v1 значения заголовка, ответ будет в соответствии с версия v1

curl -L -X GET 'http://localhost:8080/users' \
-H 'Accept: application/vnd.v1+json'

[
    {
        "name": "Abraham Lincoln"
    }
]

Когда запрос скручивания сделан с v2 значения заголовка ответ будет соответствовать версии v2

curl -L -X GET 'http://localhost:8080/users' \
-H 'Accept: application/vnd.v2+json'

[
    {
        "name": {
            "firstName": "Abraham",
            "lastName": "Lincoln"
        }
    }
]

Когда отправлено неверное значение заголовка, он ответит 406 Недопустимо

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 не отправляется, он отвечает версией по умолчанию, ie v1 здесь

curl -L -X GET 'http://localhost:8080/users'

[
    {
        "name": "Abraham Lincoln"
    }
]

Даже GitHub реализовал управление версиями с согласованием контента аналогичным образом, и вы можете посмотреть на это в их документации .

...