一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - SpringBoot如何實現接口版本控制

SpringBoot如何實現接口版本控制

2022-02-17 14:46Zacxxx Java教程

這篇文章主要介紹了SpringBoot如何實現接口版本控制,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

SpringBoot 接口版本控制

一個系統在上線后會不斷迭代更新,需求也會不斷變化,有可能接口的參數也會發生變化,如果在原有的參數上直接修改,可能會影響到現有項目的正常運行,這時我們就需要設置不同的版本,這樣即使參數發生變化,由于老版本沒有變化,因此不會影響上線系統的運行。

這里我們選擇使用帶有一位小數的浮點數作為版本號,在請求地址末尾中帶上版本號,大致的地址如:http://api/test/1.0,其中,1.0即代表的是版本號。具體做法請看代碼

自定義一個版本號的注解接口ApiVersion.java

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import org.springframework.web.bind.annotation.Mapping;
import java.lang.annotation.*;
/**
 * 版本控制
 * @author Zac
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface ApiVersion {
    /**
     * 標識版本號
     * @return
     */
    double value();
}

版本號篩選器ApiVersionCondition

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import org.springframework.web.servlet.mvc.condition.RequestCondition;
import javax.servlet.http.HttpServletRequest;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
 * 版本號匹配篩選器
 * @author Zac
 */
public class ApiVersionCondition implements RequestCondition<ApiVersionCondition> {
 
    /**
     * 路徑中版本的正則表達式匹配, 這里用 /1.0的形式
     */
    private static final Pattern VERSION_PREFIX_PATTERN = Pattern.compile("^\\S+/([1-9][.][0-9])$");
    private double apiVersion;
     public ApiVersionCondition(double apiVersion) {
        this.apiVersion = apiVersion;
    }
 
    @Override
    public ApiVersionCondition combine(ApiVersionCondition other) {
        // 采用最后定義優先原則,則方法上的定義覆蓋類上面的定義
        return new ApiVersionCondition(other.getApiVersion());
    }
 
    @Override public ApiVersionCondition getMatchingCondition(HttpServletRequest request) {
         Matcher m = VERSION_PREFIX_PATTERN.matcher(request.getRequestURI());
        if (m.find()) {
            Double version = Double.valueOf(m.group(1));
            // 如果請求的版本號大于配置版本號, 則滿足
            if (version >= this.apiVersion) {
                return this;
            }
        }
        return null;
    }
 
    @Override
    public int compareTo(ApiVersionCondition other, HttpServletRequest request) {
        // 優先匹配最新的版本號
        return Double.compare(other.getApiVersion(), this.apiVersion);
    }
    public double getApiVersion() {
        return apiVersion;
    }
}

版本號匹配攔截器

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.servlet.mvc.condition.RequestCondition;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.lang.reflect.Method;
 
/**
 * 版本號匹配攔截器
 * @author Zac
 */
public class CustomRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
     @Override protected RequestCondition<ApiVersionCondition> getCustomTypeCondition(Class<?> handlerType) {
        ApiVersion apiVersion = AnnotationUtils.findAnnotation(handlerType, ApiVersion.class);
        return createCondition(apiVersion);
    }
 
    @Override protected RequestCondition<ApiVersionCondition> getCustomMethodCondition(Method method) {
        ApiVersion apiVersion = AnnotationUtils.findAnnotation(method, ApiVersion.class);
        return createCondition(apiVersion);
    }
    private RequestCondition<ApiVersionCondition> createCondition(ApiVersion apiVersion) {
        return apiVersion == null ? null : new ApiVersionCondition(apiVersion.value());
    
}

配置WebMvcRegistrationsConfig

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.web.WebMvcRegistrationsAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
 
@SpringBootConfiguration
public class WebMvcRegistrationsConfig extends WebMvcRegistrationsAdapter {
 
    @Override
    public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
        return new CustomRequestMappingHandlerMapping();
    
}
controller層實現
/**
     * version
     * @return
     */
    @GetMapping("/api/test/{version}")
    @ApiVersion(2.0)
    public String searchTargetImage() {
        return "Hello! Welcome to Version2";
    
/**
     * 多目標類型搜索,關聯圖片查詢接口
     *
     * @param attribute
     * @return
     */
    @GetMapping("/api/test/{version}")
    @ApiVersion(1.0)
    public AppResult searchTargetImage() {
        return "Hello! Welcome to Version1";
    }

SpringBoot 2.x 接口多版本

準備將現有的接口加上版本管理,兼容以前的版本。網上一調研,發現有很多示例,但是還是存在以下兩個問題。

1.大部分使用Integer作為版本號,但是通常的版本號形式為v1.0.0,

2.版本號攜帶在header中,對接調用不清晰。

針對以上兩個問題,做如下改造。

1.自定義接口版本注解ApiVersion

后面條件映射使用equals匹配,此處是否將String變為String[]應對多個版本使用同一代碼的問題。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.yugioh.api.common.core.version;
import org.springframework.web.bind.annotation.Mapping;
import java.lang.annotation.*;
/**
 * 接口版本
 *
 * @author lieber
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface ApiVersion { 
    String value() default "1.0.0";
}

2.請求映射條件ApiVersionCondition

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package com.yugioh.api.common.core.version;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.mvc.condition.RequestCondition;
import javax.servlet.http.HttpServletRequest;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
 * 版本控制
 *
 * @author lieber
 */
@AllArgsConstructor
@Getter
@Slf4j
public class ApiVersionCondition implements RequestCondition<ApiVersionCondition> {
    private String version;
    private final static Pattern VERSION_PREFIX_PATTERN = Pattern.compile(".*v(\\d+(.\\d+){0,2}).*");
    public final static String API_VERSION_CONDITION_NULL_KEY = "API_VERSION_CONDITION_NULL_KEY";
    @Override
    public ApiVersionCondition combine(ApiVersionCondition other) {
        // 方法上的注解優于類上的注解
        return new ApiVersionCondition(other.getVersion());
    }
    @Override
    public ApiVersionCondition getMatchingCondition(HttpServletRequest request) {
        Matcher m = VERSION_PREFIX_PATTERN.matcher(request.getRequestURI());
        if (m.find()) {
            String version = m.group(1);
            if (this.compareTo(version)) {
                return this;
            }
        }
        // 將錯誤放在request中,可以在錯誤頁面明確提示,此處可重構為拋出運行時異常
        request.setAttribute(API_VERSION_CONDITION_NULL_KEY, true);
        return null;
    }
    @Override
    public int compareTo(ApiVersionCondition other, HttpServletRequest request) {
        return this.compareTo(other.getVersion()) ? 1 : -1;
    }
    private boolean compareTo(String version) {
        return Objects.equals(version, this.version);
    }
}

3.創建自定義匹配處理器ApiVersionRequestMappingHandlerMapping

網上大部分只重寫了getCustomTypeCondition和getCustomMethodCondition方法。這里為了解決路由映射問題,重寫getMappingForMethod方法,在路由中加入前綴{version},加入后路由變為/api/{version}/xxx

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package com.yugioh.api.common.core.version;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.servlet.mvc.condition.RequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.lang.reflect.Method;
/**
 * 自定義匹配處理器
 *
 * @author lieber
 */
public class ApiVersionRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
    private final static String VERSION_PREFIX = "{version}";
    @Override
    protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) {
        ApiVersion apiVersion = AnnotationUtils.findAnnotation(handlerType, ApiVersion.class);
        return createCondition(apiVersion);
    }
    @Override
    protected RequestCondition<?> getCustomMethodCondition(Method method) {
        ApiVersion apiVersion = AnnotationUtils.findAnnotation(method, ApiVersion.class);
        return createCondition(apiVersion);
    }
    @Override
    protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
        RequestMappingInfo requestMappingInfo = super.getMappingForMethod(method, handlerType);
        if (requestMappingInfo == null) {
            return null;
        }
        return createCustomRequestMappingInfo(method, handlerType, requestMappingInfo);
    }
    private RequestMappingInfo createCustomRequestMappingInfo(Method method, Class<?> handlerType, RequestMappingInfo requestMappingInfo) {
        ApiVersion methodApi = AnnotatedElementUtils.findMergedAnnotation(method, ApiVersion.class);
        ApiVersion handlerApi = AnnotatedElementUtils.findMergedAnnotation(handlerType, ApiVersion.class);
        if (methodApi != null || handlerApi != null) {
            return RequestMappingInfo.paths(VERSION_PREFIX).options(this.config).build().combine(requestMappingInfo);
        }
        return requestMappingInfo;
    }
    private RequestMappingInfo.BuilderConfiguration config = new RequestMappingInfo.BuilderConfiguration();
    private RequestCondition<ApiVersionCondition> createCondition(ApiVersion apiVersion) {
        return apiVersion == null ? null : new ApiVersionCondition(apiVersion.value());
    }
}

4.使用ApiVersionConfig配置來決定是否開啟多版本

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.yugioh.api.common.core.version;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
 * 版本管理器配置
 *
 * @author lieber
 */
@Data
@Configuration
@ConfigurationProperties(prefix = "api.config.version", ignoreInvalidFields = true)
public class ApiVersionConfig {
    /**
     * 是否開啟
     */
    private boolean enable;
}

5.配置WebMvcRegistrations,啟用自定義路由Mapping

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.yugioh.api.common.core.version;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
/**
 * @author lieber
 */
@Configuration
@Order(Ordered.HIGHEST_PRECEDENCE)
public class ApiWebMvcRegistrations implements WebMvcRegistrations {
    private final ApiVersionConfig apiVersionConfig;
    @Autowired
    public ApiWebMvcRegistrations(ApiVersionConfig apiVersionConfig) {
        this.apiVersionConfig = apiVersionConfig;
    }
    @Override
    public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
        if (apiVersionConfig.isEnable()) {
            return new ApiVersionRequestMappingHandlerMapping();
        }
        return null;
    }
}

至此我們就能在項目中愉快的使用@APIVersion來指定版本了,但是現在這個和swagger整合還會有問題,繼續研究中…

以上為個人經驗,希望能給大家一個參考,也希望大家多多支持服務器之家。

原文鏈接:https://blog.csdn.net/asd120829243/article/details/90175930

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 国产高清国内精品福利色噜噜 | 亚洲美女aⅴ久久久91 | 四虎永久在线精品波多野结衣 | 国产一级毛片国语版 | 毛片a级放荡的护士hd | 国产精品美女福利视频免费专区 | 欧美成人午夜片一一在线观看 | 亚洲品质自拍视频网站 | 女烈受刑重口小说 | 女性全身裸露无遮挡 | 免费视频专区一国产盗摄 | 特黄特黄一级高清免费大片 | 把美女屁股眼扒开图片 | 91香蕉国产在线观看人员 | 国产精品日韩欧美一区二区三区 | 5g影院天天爽爽 | 色吧五月婷婷 | 北条麻妃黑人正在播放 | 精品日韩二区三区精品视频 | 国产清纯白嫩大学生正在播放 | 91李宗精品72集在线观看 | 午夜久久久久久网站 | 国内精品91久久久久 | 国产婷婷综合丁香亚洲欧洲 | 国产精品成人免费观看 | 国产成人免费片在线视频观看 | 我和寂寞孕妇的性事 | 国产欧美精品一区二区三区四区 | 亚洲四虎 | 国产精品酒店视频免费看 | 久久电影院久久国产 | 春意影院午夜爽爽爽免费 | 黑人干我| 国产一二在线观看视频网站 | 桃乃木香奈作品在线 | 色婷婷天天综合在线 | 免费日批| 新新电影理论中文字幕 | 变态女王麻麻小说在线阅读 | a一区二区三区视频 | 国产香蕉97碰碰在线视频 |