激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

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

香港云服务器
服務(wù)器之家 - 編程語言 - Java教程 - SpringBoot如何實(shí)現(xiàn)接口版本控制

SpringBoot如何實(shí)現(xiàn)接口版本控制

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

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

SpringBoot 接口版本控制

一個(gè)系統(tǒng)在上線后會(huì)不斷迭代更新,需求也會(huì)不斷變化,有可能接口的參數(shù)也會(huì)發(fā)生變化,如果在原有的參數(shù)上直接修改,可能會(huì)影響到現(xiàn)有項(xiàng)目的正常運(yùn)行,這時(shí)我們就需要設(shè)置不同的版本,這樣即使參數(shù)發(fā)生變化,由于老版本沒有變化,因此不會(huì)影響上線系統(tǒng)的運(yùn)行。

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

自定義一個(gè)版本號(hào)的注解接口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 {
    /**
     * 標(biāo)識(shí)版本號(hào)
     * @return
     */
    double value();
}

版本號(hào)篩選器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;
/**
 * 版本號(hào)匹配篩選器
 * @author Zac
 */
public class ApiVersionCondition implements RequestCondition<ApiVersionCondition> {
 
    /**
     * 路徑中版本的正則表達(dá)式匹配, 這里用 /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) {
        // 采用最后定義優(yōu)先原則,則方法上的定義覆蓋類上面的定義
        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));
            // 如果請(qǐng)求的版本號(hào)大于配置版本號(hào), 則滿足
            if (version >= this.apiVersion) {
                return this;
            }
        }
        return null;
    }
 
    @Override
    public int compareTo(ApiVersionCondition other, HttpServletRequest request) {
        // 優(yōu)先匹配最新的版本號(hào)
        return Double.compare(other.getApiVersion(), this.apiVersion);
    }
    public double getApiVersion() {
        return apiVersion;
    }
}

版本號(hào)匹配攔截器

?
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;
 
/**
 * 版本號(hào)匹配攔截器
 * @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層實(shí)現(xiàn)
/**
     * version
     * @return
     */
    @GetMapping("/api/test/{version}")
    @ApiVersion(2.0)
    public String searchTargetImage() {
        return "Hello! Welcome to Version2";
    
/**
     * 多目標(biāo)類型搜索,關(guān)聯(lián)圖片查詢接口
     *
     * @param attribute
     * @return
     */
    @GetMapping("/api/test/{version}")
    @ApiVersion(1.0)
    public AppResult searchTargetImage() {
        return "Hello! Welcome to Version1";
    }

SpringBoot 2.x 接口多版本

準(zhǔn)備將現(xiàn)有的接口加上版本管理,兼容以前的版本。網(wǎng)上一調(diào)研,發(fā)現(xiàn)有很多示例,但是還是存在以下兩個(gè)問題。

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

2.版本號(hào)攜帶在header中,對(duì)接調(diào)用不清晰。

針對(duì)以上兩個(gè)問題,做如下改造。

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

后面條件映射使用equals匹配,此處是否將String變?yōu)镾tring[]應(yīng)對(duì)多個(gè)版本使用同一代碼的問題。

?
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.請(qǐng)求映射條件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) {
        // 方法上的注解優(yōu)于類上的注解
        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;
            }
        }
        // 將錯(cuò)誤放在request中,可以在錯(cuò)誤頁面明確提示,此處可重構(gòu)為拋出運(yùn)行時(shí)異常
        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.創(chuàng)建自定義匹配處理器ApiVersionRequestMappingHandlerMapping

網(wǎng)上大部分只重寫了getCustomTypeCondition和getCustomMethodCondition方法。這里為了解決路由映射問題,重寫getMappingForMethod方法,在路由中加入前綴{version},加入后路由變?yōu)?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;
    }
}

至此我們就能在項(xiàng)目中愉快的使用@APIVersion來指定版本了,但是現(xiàn)在這個(gè)和swagger整合還會(huì)有問題,繼續(xù)研究中…

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持服務(wù)器之家。

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

延伸 · 閱讀

精彩推薦
1472
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23 Weibo Article 24 Weibo Article 25
主站蜘蛛池模板: 亚洲精品日韩色噜噜久久五月 | 特大黑人videos与另类娇小 | 男女一边摸一边做羞羞视频免费 | 99re66热这里只有精品8 | 美女视频网站黄色 | 欧美视频一区二区三区四区 | 狠狠操人人干 | 爱逼av | 一级黄色电影网站 | 久久久久久久久久久亚洲 | 亚洲免费视| 香蕉久久久精品 | 色淫影院| 成人免费电影av | 欧美成人国产va精品日本一级 | 久在线观看福利视频69 | 美女擦逼 | 国产a级网站 | 无遮挡一级毛片视频 | 91精品最新国内在线播放 | 97久久精品一区二区三区观看 | 31freehdxxxx欧美| 奇米影视奇奇米色狠狠色777 | 日韩字幕 | japan护士性xxxⅹhd | 青久草视频| 国产精品免费一区二区三区四区 | 久草在线资源视频 | 看a级毛片 | 一级成人欧美一区在线观看 | 日本高清黄色片 | 香蕉秀| 男女无遮挡羞羞视频 | 一级黄色在线观看 | 国产69精品99久久久久久宅男 | 色播视频网站 | 一级大片在线观看 | 亚洲一区二区三区视频 | 秋霞a级毛片在线看 | 日韩色视频在线观看 | 国产91对白叫床清晰播放 |