├── .gitignore ├── LICENSE ├── README.md ├── pom.xml └── src ├── main └── java │ └── com │ └── github │ └── lkqm │ └── spring │ └── api │ └── version │ ├── ApiVersion.java │ ├── ApiVersionAutoConfiguration.java │ ├── ApiVersionProperties.java │ ├── ApiVersionRequestCondition.java │ ├── ApiVersionWebMvcRegistrations.java │ ├── EnableApiVersioning.java │ ├── InnerUtils.java │ └── VersionedRequestMappingHandlerMapping.java └── test └── java └── com └── github └── lkqm └── spring └── api └── version ├── Application.java ├── InnerUtilsTest.java ├── controller └── UserController.java └── integration ├── UserControllerHeaderTest.java ├── UserControllerParamTest.java ├── UserControllerUrIEndTest.java └── UserControllerUrITest.java /.gitignore: -------------------------------------------------------------------------------- 1 | # maven ignore 2 | target/ 3 | *.jar 4 | !.mvn/wrapper/* 5 | *.war 6 | *.zip 7 | *.tar 8 | *.tar.gz 9 | 10 | # eclipse ignore 11 | .settings/ 12 | .project 13 | .classpath 14 | 15 | # idea ignore 16 | .idea/ 17 | *.ipr 18 | *.iml 19 | *.iws 20 | 21 | # temp ignore 22 | *.log 23 | *.cache 24 | *.diff 25 | *.patch 26 | *.tmp 27 | 28 | # system ignore 29 | .DS_Store 30 | Thumbs.db 31 | *.orig 32 | *.out -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Mario Luo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # spring-api-versioning 2 | ![Maven Central](https://img.shields.io/maven-central/v/com.github.lkqm/spring-api-versioning) 3 | ![Travis (.org) branch](https://img.shields.io/travis/lkqm/spring-api-versioning/master) 4 | 5 | Simple development of multi-version api based on spring-mvc via @ApiVersion annotation, support for uri, header, param. 6 | 7 | Supports: JDK 1.8, spring-boot 2.x 8 | 9 | ## Features 10 | - URI: /v1/user/list, /v2/user/list 11 | - Header: /user/list, header: X-API-VERSION=1 12 | - Param: /user/list?api_version=1 13 | 14 | Important: version number use precise matching with String equals method. 15 | 16 | ## Quick 17 | 1. Add Dependency(Maven) 18 | ``` 19 | 20 | com.github.lkqm 21 | spring-api-versioning 22 | ${version} 23 | 24 | ``` 25 | 26 | 2. @EnableApiVersioning with Application class 27 | ``` 28 | @SpringBootApplication 29 | @EnableApiVersioning 30 | public class Application { 31 | public static void main(String[] args) { 32 | SpringApplication.run(Application.class, args); 33 | } 34 | } 35 | ``` 36 | 37 | 3. Controller 38 | ``` 39 | @RestController 40 | @RequestMapping("/user") 41 | @ApiVersion("1") 42 | public class UserController { 43 | 44 | @GetMapping("/list") 45 | public String list1() { 46 | return "list1"; 47 | } 48 | 49 | @GetMapping("/list") 50 | @ApiVersion("1.1") 51 | public String list2() { 52 | return "list2"; 53 | } 54 | } 55 | ``` 56 | 4. Test 57 | ``` 58 | curl http://127.0.0.1:8080/v1/user/list 59 | curl http://127.0.0.1:8080/v1.1/user/list 60 | ``` 61 | 62 | 63 | ## Config properties 64 | ``` 65 | api.version.type=uri # versioning implement way: uri(default), header, param 66 | api.version.uri-prefix= # uri prefix, if set /api, request uri like: /api/v1/... /api/v2/... 67 | api.version.uri-location= # uri version location: begin(/v1/user/list), end(/user/list/v1) 68 | api.version.header=X-API-VERSION # version control http header name 69 | api.version.param=api_version # version control http query string name 70 | ``` -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | com.github.lkqm 6 | springboot3-api-versioning 7 | 1.0.0 8 | 9 | ${project.artifactId} 10 | Simple development of multi-version api based on spring-mvc via @ApiVersion annotation, support for 11 | uri, header, param. 12 | 13 | https://github.com/lkqm/spring-api-versioning 14 | 15 | 16 | The Apache License, Version 2.0 17 | http://www.apache.org/licenses/LICENSE-2.0.txt 18 | 19 | 20 | 21 | 22 | Mario Luo 23 | luokaiqiongmou@foxmail.com 24 | https://github.com/lkqm 25 | 26 | 27 | 28 | scm:git:https://github.com/lkqm/spring-api-versioning.git 29 | scm:git:git@github.com:lkqm/spring-api-versioning.git 30 | https://github.com/lkqm/spring-api-versioning 31 | HEAD 32 | 33 | 34 | 17 35 | 17 36 | UTF-8 37 | UTF-8 38 | 3.1.5 39 | 40 | 41 | 42 | 43 | 44 | org.springframework.boot 45 | spring-boot-starter 46 | ${spring.version} 47 | true 48 | 49 | 50 | org.springframework.boot 51 | spring-boot-configuration-processor 52 | ${spring.version} 53 | true 54 | 55 | 56 | org.springframework.boot 57 | spring-boot-starter-web 58 | ${spring.version} 59 | true 60 | 61 | 62 | org.springframework.boot 63 | spring-boot-starter-test 64 | ${spring.version} 65 | test 66 | 67 | 68 | 69 | org.projectlombok 70 | lombok 71 | 1.18.30 72 | provided 73 | 74 | 75 | 76 | 77 | 78 | release 79 | 80 | 81 | 82 | org.apache.maven.plugins 83 | maven-source-plugin 84 | 2.2.1 85 | 86 | 87 | attach-sources 88 | 89 | jar-no-fork 90 | 91 | 92 | 93 | 94 | 95 | org.apache.maven.plugins 96 | maven-javadoc-plugin 97 | 2.9.1 98 | 99 | 100 | attach-javadocs 101 | 102 | jar 103 | 104 | 105 | -Xdoclint:none 106 | 107 | 108 | 109 | 110 | 111 | org.apache.maven.plugins 112 | maven-gpg-plugin 113 | 1.5 114 | 115 | 116 | sign-artifacts 117 | verify 118 | 119 | sign 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | org.apache.maven.plugins 133 | maven-release-plugin 134 | 2.5.3 135 | 136 | v@{project.version} 137 | true 138 | false 139 | -DskipTests 140 | 141 | 142 | 143 | org.sonatype.plugins 144 | nexus-staging-maven-plugin 145 | 1.6.7 146 | true 147 | 148 | ossrh 149 | https://oss.sonatype.org/ 150 | true 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | ossrh 159 | Nexus Releases Repository 160 | https://oss.sonatype.org/service/local/staging/deploy/maven2 161 | 162 | 163 | ossrh 164 | Nexus Snapshots Repository 165 | https://oss.sonatype.org/content/repositories/snapshots 166 | 167 | 168 | 169 | 170 | -------------------------------------------------------------------------------- /src/main/java/com/github/lkqm/spring/api/version/ApiVersion.java: -------------------------------------------------------------------------------- 1 | package com.github.lkqm.spring.api.version; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * 标示当前请求版本 10 | */ 11 | @Target({ElementType.TYPE, ElementType.METHOD}) 12 | @Retention(RetentionPolicy.RUNTIME) 13 | public @interface ApiVersion { 14 | 15 | /** 16 | * 版本号 17 | */ 18 | String value(); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/github/lkqm/spring/api/version/ApiVersionAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.github.lkqm.spring.api.version; 2 | 3 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 4 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | /** 9 | * 配置api版本 10 | */ 11 | @Configuration 12 | @EnableConfigurationProperties(ApiVersionProperties.class) 13 | public class ApiVersionAutoConfiguration { 14 | 15 | @Bean 16 | @ConditionalOnMissingBean 17 | public ApiVersionWebMvcRegistrations apiVersionWebMvcRegistrations(ApiVersionProperties apiVersionProperties) { 18 | return new ApiVersionWebMvcRegistrations(apiVersionProperties); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/github/lkqm/spring/api/version/ApiVersionProperties.java: -------------------------------------------------------------------------------- 1 | package com.github.lkqm.spring.api.version; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | 6 | import java.io.Serializable; 7 | 8 | /** 9 | * Api-Version配置 10 | */ 11 | @Data 12 | @ConfigurationProperties(prefix = "api.version") 13 | public class ApiVersionProperties implements Serializable { 14 | 15 | /** 16 | * 实现多版本的方式 17 | */ 18 | private Type type = Type.URI; 19 | 20 | /** 21 | * URI地址前缀, 例如: /api 22 | */ 23 | private String uriPrefix; 24 | 25 | /** 26 | * URI的位置 27 | */ 28 | private UriLocation uriLocation = UriLocation.BEGIN; 29 | 30 | /** 31 | * 版本请求头名 32 | */ 33 | private String header = "X-API-VERSION"; 34 | 35 | /** 36 | * 版本请求参数名 37 | */ 38 | private String param = "api_version"; 39 | 40 | public enum Type { 41 | /** 42 | * URI路径 43 | */ 44 | URI, 45 | /** 46 | * 请求头 47 | */ 48 | HEADER, 49 | /** 50 | * 请求参数 51 | */ 52 | PARAM; 53 | } 54 | 55 | public enum UriLocation { 56 | BEGIN, END 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/github/lkqm/spring/api/version/ApiVersionRequestCondition.java: -------------------------------------------------------------------------------- 1 | package com.github.lkqm.spring.api.version; 2 | 3 | import jakarta.servlet.http.HttpServletRequest; 4 | import lombok.Getter; 5 | import lombok.NonNull; 6 | import org.springframework.web.servlet.mvc.condition.RequestCondition; 7 | 8 | 9 | @Getter 10 | public class ApiVersionRequestCondition implements RequestCondition { 11 | 12 | private final String apiVersion; 13 | private final ApiVersionProperties apiVersionProperties; 14 | 15 | public ApiVersionRequestCondition(@NonNull String apiVersion, @NonNull ApiVersionProperties apiVersionProperties) { 16 | this.apiVersion = apiVersion.trim(); 17 | this.apiVersionProperties = apiVersionProperties; 18 | } 19 | 20 | @Override 21 | public ApiVersionRequestCondition combine(ApiVersionRequestCondition other) { 22 | // method annotation first 23 | return new ApiVersionRequestCondition(other.getApiVersion(), other.getApiVersionProperties()); 24 | } 25 | 26 | @Override 27 | public int compareTo(ApiVersionRequestCondition other, HttpServletRequest request) { 28 | return other.getApiVersion().compareTo(getApiVersion()); 29 | } 30 | 31 | @Override 32 | public ApiVersionRequestCondition getMatchingCondition(HttpServletRequest request) { 33 | ApiVersionProperties.Type type = apiVersionProperties.getType(); 34 | String version = null; 35 | switch (type) { 36 | case HEADER: 37 | version = request.getHeader(apiVersionProperties.getHeader()); 38 | break; 39 | case PARAM: 40 | version = request.getParameter(apiVersionProperties.getParam()); 41 | break; 42 | } 43 | boolean match = version != null && version.length() > 0 && version.trim().equals(apiVersion); 44 | if (match) { 45 | return this; 46 | } 47 | return null; 48 | } 49 | 50 | @Override 51 | public String toString() { 52 | return "@ApiVersion(" + apiVersion + ")"; 53 | } 54 | } -------------------------------------------------------------------------------- /src/main/java/com/github/lkqm/spring/api/version/ApiVersionWebMvcRegistrations.java: -------------------------------------------------------------------------------- 1 | package com.github.lkqm.spring.api.version; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.NonNull; 5 | import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations; 6 | import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; 7 | 8 | 9 | @AllArgsConstructor 10 | public class ApiVersionWebMvcRegistrations implements WebMvcRegistrations { 11 | 12 | @NonNull 13 | private ApiVersionProperties apiVersionProperties; 14 | 15 | @Override 16 | public RequestMappingHandlerMapping getRequestMappingHandlerMapping() { 17 | return new VersionedRequestMappingHandlerMapping(apiVersionProperties); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/github/lkqm/spring/api/version/EnableApiVersioning.java: -------------------------------------------------------------------------------- 1 | package com.github.lkqm.spring.api.version; 2 | 3 | import org.springframework.context.annotation.Import; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | /** 11 | * 开启多版本API控制 12 | * 13 | * @see ApiVersionProperties 配置属性 14 | * @see ApiVersionAutoConfiguration 配置类 15 | */ 16 | @Target(ElementType.TYPE) 17 | @Retention(RetentionPolicy.RUNTIME) 18 | @Import(ApiVersionAutoConfiguration.class) 19 | public @interface EnableApiVersioning { 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/github/lkqm/spring/api/version/InnerUtils.java: -------------------------------------------------------------------------------- 1 | package com.github.lkqm.spring.api.version; 2 | 3 | import java.util.regex.Pattern; 4 | 5 | class InnerUtils { 6 | 7 | private final static Pattern VERSION_NUMBER_PATTERN = Pattern.compile("^\\d+(\\.\\d+){0,2}$"); 8 | 9 | /** 10 | * 检查版本匹配是否复合(最大三个版本) 11 | */ 12 | public static void checkVersionNumber(String version, Object targetMethodOrType) { 13 | if (!matchVersionNumber(version)) { 14 | throw new IllegalArgumentException(String.format("Invalid version number: @ApiVersion(\"%s\") at %s", version, targetMethodOrType)); 15 | } 16 | } 17 | 18 | /** 19 | * 判断是否满足最大3个版本号的匹配 20 | */ 21 | public static boolean matchVersionNumber(String version) { 22 | return version.length() != 0 && VERSION_NUMBER_PATTERN.matcher(version).find(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/github/lkqm/spring/api/version/VersionedRequestMappingHandlerMapping.java: -------------------------------------------------------------------------------- 1 | package com.github.lkqm.spring.api.version; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.core.annotation.AnnotatedElementUtils; 6 | import org.springframework.core.annotation.AnnotationUtils; 7 | import org.springframework.util.StringUtils; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.servlet.mvc.condition.RequestCondition; 10 | import org.springframework.web.servlet.mvc.method.RequestMappingInfo; 11 | import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; 12 | 13 | import java.lang.reflect.AnnotatedElement; 14 | import java.lang.reflect.Method; 15 | 16 | @Slf4j 17 | @AllArgsConstructor 18 | public class VersionedRequestMappingHandlerMapping extends RequestMappingHandlerMapping { 19 | 20 | /** 21 | * 多版本配置属性 22 | */ 23 | private ApiVersionProperties apiVersionProperties; 24 | 25 | @Override 26 | protected RequestCondition getCustomTypeCondition(Class handlerType) { 27 | return createRequestCondition(handlerType); 28 | } 29 | 30 | @Override 31 | protected RequestCondition getCustomMethodCondition(Method method) { 32 | return createRequestCondition(method); 33 | } 34 | 35 | private RequestCondition createRequestCondition(AnnotatedElement target) { 36 | if (apiVersionProperties.getType() == ApiVersionProperties.Type.URI) { 37 | return null; 38 | } 39 | ApiVersion apiVersion = AnnotationUtils.findAnnotation(target, ApiVersion.class); 40 | if (apiVersion == null) { 41 | return null; 42 | } 43 | String version = apiVersion.value().trim(); 44 | InnerUtils.checkVersionNumber(version, target); 45 | return new ApiVersionRequestCondition(version, apiVersionProperties); 46 | } 47 | 48 | //--------------------- 动态注册URI -----------------------// 49 | @Override 50 | protected RequestMappingInfo getMappingForMethod(Method method, Class handlerType) { 51 | RequestMappingInfo info = this.createRequestMappingInfo(method); 52 | if (info != null) { 53 | RequestMappingInfo typeInfo = this.createRequestMappingInfo(handlerType); 54 | if (typeInfo != null) { 55 | info = typeInfo.combine(info); 56 | } 57 | 58 | // 指定URL前缀 59 | if (apiVersionProperties.getType() == ApiVersionProperties.Type.URI) { 60 | ApiVersion apiVersion = AnnotationUtils.getAnnotation(method, ApiVersion.class); 61 | if (apiVersion == null) { 62 | apiVersion = AnnotationUtils.getAnnotation(handlerType, ApiVersion.class); 63 | } 64 | if (apiVersion != null) { 65 | String version = apiVersion.value().trim(); 66 | InnerUtils.checkVersionNumber(version, method); 67 | 68 | String prefix = "/v" + version; 69 | if (apiVersionProperties.getUriLocation() == ApiVersionProperties.UriLocation.END) { 70 | info = info.combine(RequestMappingInfo.paths(prefix).options(getBuilderConfiguration()).build()); 71 | } else { 72 | if (StringUtils.hasText(apiVersionProperties.getUriPrefix())) { 73 | prefix = apiVersionProperties.getUriPrefix().trim() + prefix; 74 | } 75 | info = RequestMappingInfo.paths(prefix).options(getBuilderConfiguration()).build().combine(info); 76 | } 77 | } 78 | } 79 | } 80 | 81 | return info; 82 | } 83 | 84 | private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) { 85 | RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class); 86 | RequestCondition condition = (element instanceof Class ? 87 | getCustomTypeCondition((Class) element) : getCustomMethodCondition((Method) element)); 88 | return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null); 89 | } 90 | 91 | } -------------------------------------------------------------------------------- /src/test/java/com/github/lkqm/spring/api/version/Application.java: -------------------------------------------------------------------------------- 1 | package com.github.lkqm.spring.api.version; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | @EnableApiVersioning 8 | public class Application { 9 | 10 | public static void main(String[] args) { 11 | SpringApplication.run(Application.class, args); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/test/java/com/github/lkqm/spring/api/version/InnerUtilsTest.java: -------------------------------------------------------------------------------- 1 | package com.github.lkqm.spring.api.version; 2 | 3 | 4 | import org.junit.jupiter.api.Assertions; 5 | import org.junit.jupiter.api.Test; 6 | 7 | public class InnerUtilsTest { 8 | 9 | @Test 10 | public void matchVersionNumber() { 11 | Assertions.assertTrue(InnerUtils.matchVersionNumber("1")); 12 | Assertions.assertTrue(InnerUtils.matchVersionNumber("1.1")); 13 | Assertions.assertTrue(InnerUtils.matchVersionNumber("1.1.1")); 14 | 15 | Assertions.assertFalse(InnerUtils.matchVersionNumber("1.1.1.0")); 16 | Assertions.assertFalse(InnerUtils.matchVersionNumber("")); 17 | Assertions.assertFalse(InnerUtils.matchVersionNumber(".")); 18 | Assertions.assertFalse(InnerUtils.matchVersionNumber("a.b")); 19 | } 20 | } -------------------------------------------------------------------------------- /src/test/java/com/github/lkqm/spring/api/version/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package com.github.lkqm.spring.api.version.controller; 2 | 3 | import com.github.lkqm.spring.api.version.ApiVersion; 4 | import org.springframework.web.bind.annotation.GetMapping; 5 | import org.springframework.web.bind.annotation.RequestMapping; 6 | import org.springframework.web.bind.annotation.RestController; 7 | 8 | @RestController 9 | @RequestMapping("/user") 10 | public class UserController { 11 | 12 | @GetMapping("/list") 13 | @ApiVersion("1") 14 | public String list1() { 15 | return "list1"; 16 | } 17 | 18 | @GetMapping("/list") 19 | @ApiVersion("1.1") 20 | public String list2() { 21 | return "list2"; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/test/java/com/github/lkqm/spring/api/version/integration/UserControllerHeaderTest.java: -------------------------------------------------------------------------------- 1 | package com.github.lkqm.spring.api.version.integration; 2 | 3 | import com.github.lkqm.spring.api.version.Application; 4 | import org.junit.jupiter.api.Test; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; 7 | import org.springframework.boot.test.context.SpringBootTest; 8 | import org.springframework.test.web.servlet.MockMvc; 9 | 10 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; 11 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; 12 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 13 | 14 | @SpringBootTest(classes = Application.class, properties = { 15 | "api.version.type=header", 16 | "api.version.header=X-API-VERSION" 17 | }) 18 | @AutoConfigureMockMvc 19 | public class UserControllerHeaderTest { 20 | 21 | @Autowired 22 | private MockMvc mockMvc; 23 | 24 | @Test 25 | public void listV1() throws Exception { 26 | mockMvc.perform(get("/user/list").header("X-API-VERSION", "1")) 27 | .andExpect(status().isOk()) 28 | .andExpect(content().string("list1")); 29 | } 30 | 31 | @Test 32 | public void listV2() throws Exception { 33 | mockMvc.perform(get("/user/list").header("X-API-VERSION", "1.1")) 34 | .andExpect(status().isOk()) 35 | .andExpect(content().string("list2")); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/test/java/com/github/lkqm/spring/api/version/integration/UserControllerParamTest.java: -------------------------------------------------------------------------------- 1 | package com.github.lkqm.spring.api.version.integration; 2 | 3 | import com.github.lkqm.spring.api.version.Application; 4 | import org.junit.jupiter.api.Test; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; 7 | import org.springframework.boot.test.context.SpringBootTest; 8 | import org.springframework.test.web.servlet.MockMvc; 9 | 10 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; 11 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; 12 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 13 | 14 | @SpringBootTest(classes = Application.class, properties = { 15 | "api.version.type=param", 16 | "api.version.param=api_version" 17 | }) 18 | @AutoConfigureMockMvc 19 | public class UserControllerParamTest { 20 | 21 | @Autowired 22 | private MockMvc mockMvc; 23 | 24 | @Test 25 | public void listV1() throws Exception { 26 | mockMvc.perform(get("/user/list").param("api_version", "1")) 27 | .andExpect(status().isOk()) 28 | .andExpect(content().string("list1")); 29 | } 30 | 31 | @Test 32 | public void listV2() throws Exception { 33 | mockMvc.perform(get("/user/list").param("api_version", "1.1")) 34 | .andExpect(status().isOk()) 35 | .andExpect(content().string("list2")); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/test/java/com/github/lkqm/spring/api/version/integration/UserControllerUrIEndTest.java: -------------------------------------------------------------------------------- 1 | package com.github.lkqm.spring.api.version.integration; 2 | 3 | import com.github.lkqm.spring.api.version.Application; 4 | import org.junit.jupiter.api.Test; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; 7 | import org.springframework.boot.test.context.SpringBootTest; 8 | import org.springframework.test.web.servlet.MockMvc; 9 | 10 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; 11 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; 12 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 13 | 14 | @SpringBootTest(classes = Application.class, properties = { 15 | "api.version.type=uri", 16 | "api.version.uri-location=end", 17 | }) 18 | @AutoConfigureMockMvc 19 | public class UserControllerUrIEndTest { 20 | 21 | @Autowired 22 | private MockMvc mockMvc; 23 | 24 | @Test 25 | public void listV1() throws Exception { 26 | mockMvc.perform(get("/user/list/v1")) 27 | .andExpect(status().isOk()) 28 | .andExpect(content().string("list1")); 29 | } 30 | 31 | @Test 32 | public void listV2() throws Exception { 33 | mockMvc.perform(get("/user/list/v1.1")) 34 | .andExpect(status().isOk()) 35 | .andExpect(content().string("list2")); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/test/java/com/github/lkqm/spring/api/version/integration/UserControllerUrITest.java: -------------------------------------------------------------------------------- 1 | package com.github.lkqm.spring.api.version.integration; 2 | 3 | import com.github.lkqm.spring.api.version.Application; 4 | import org.junit.jupiter.api.Test; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; 7 | import org.springframework.boot.test.context.SpringBootTest; 8 | import org.springframework.test.web.servlet.MockMvc; 9 | 10 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; 11 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; 12 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 13 | 14 | @SpringBootTest(classes = Application.class, properties = { 15 | "api.version.type=uri", 16 | "api.version.uri-prefix=/api" 17 | }) 18 | @AutoConfigureMockMvc 19 | public class UserControllerUrITest { 20 | 21 | @Autowired 22 | private MockMvc mockMvc; 23 | 24 | @Test 25 | public void listV1() throws Exception { 26 | mockMvc.perform(get("/api/v1/user/list")) 27 | .andExpect(status().isOk()) 28 | .andExpect(content().string("list1")); 29 | } 30 | 31 | @Test 32 | public void listV2() throws Exception { 33 | mockMvc.perform(get("/api/v1.1/user/list")) 34 | .andExpect(status().isOk()) 35 | .andExpect(content().string("list2")); 36 | } 37 | 38 | } 39 | --------------------------------------------------------------------------------