Implementation of interface version support for back end in spring boot application

Time:2020-4-23

Implementation of interface version support for back end in spring boot application

Implementation of interface version support for back end in spring boot application

As a main back-end developer, the most annoying thing to do in normal work can be said to be parameter verification and interface version support. For the students on the client side, the historical burden of business will be much smaller. When incompatible business changes occur, it’s better to develop new ones directly. However, the back-end is not so simple. The historical interface needs to be supported, and the new business needs to be supported. What can we do if we add a new service interface and the URL can’t be the same as before? You can only add a similarv1, v2

Is there a way to support version management through other ways without changing the URL?

This article will introduce an instance case that uses the request header to pass the client version and finds the most suitable interface for the version request in the same URL

The main knowledge points used are:

  • RequestCondition
  • RequestMappingHandlerMapping

<!– more –>

1. Application scenario

We hope that the same business will always use the same URL. Even if the business is completely incompatible between different versions, we can select the most appropriate back-end interface through the version in the request parameter to respond to the request

1. Agreement

The above case needs to be implemented. First, there are two conventions

  • Version parameter must be carried in each request
  • Each interface is defined with a supported version

2. Rules

After making the above two premises clear, it is the basic rule

Version definition

According to the common three segment version design, the version format is defined as follows

x.x.x
  • The first X: corresponds to a large version. Generally speaking, only major changes and upgrades can change it
  • The second X: indicates the normal business iteration version number, which is + 1 for each regular app upgrade
  • Last X: mainly for bugfix. For example, when an app is released, the result is abnormal. An emergency repair is needed and another version needs to be released. At this time, the value can be + 1

Interface selection

Generally, web requests use URL matching rules to select the corresponding response interface, but in our case, a URL may have multiple different interfaces, how to choose?

  • First, from the request, get the version parameter version
  • From all the same URL interfaces, find all the interfaces less than or equal to version according to the version defined on the interface
  • Select the interface with the largest version to respond to the request

2. Application implementation

After defining the above application scenarios, design and implement

1. Interface definition

First, we need a version defined annotation to mark the version of the web service interface. The default version is 1.0.0

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Api {

    /**
     *Version
     *
     * @return
     */
    String value() default "1.0.0";
}

Next, you need an entity class corresponding to the version. Note that in the following implementation, the default version is1.0.0, andComparableInterface, support comparison between versions

@Data
public class ApiItem implements Comparable<ApiItem> {

    private int high = 1;

    private int mid = 0;

    private int low = 0;

    public ApiItem() {
    }

    @Override
    public int compareTo(ApiItem right) {
        if (this.getHigh() > right.getHigh()) {
            return 1;
        } else if (this.getHigh() < right.getHigh()) {
            return -1;
        }

        if (this.getMid() > right.getMid()) {
            return 1;
        } else if (this.getMid() < right.getMid()) {
            return -1;
        }

        if (this.getLow() > right.getLow()) {
            return 1;
        } else if (this.getLow() < right.getLow()) {
            return -1;
        }
        return 0;
    }
}

A conversion class is required to convert the string format version to apiitem, and the default version is supported1.0.0Setting of

public class ApiConverter {
    public static ApiItem convert(String api) {
        ApiItem apiItem = new ApiItem();
        if (StringUtils.isBlank(api)) {
            return apiItem;
        }

        String[] cells = StringUtils.split(api, ".");
        apiItem.setHigh(Integer.parseInt(cells[0]));
        if (cells.length > 1) {
            apiItem.setMid(Integer.parseInt(cells[1]));
        }

        if (cells.length > 2) {
            apiItem.setLow(Integer.parseInt(cells[2]));
        }
        return apiItem;
    }
}

2. Handlermapping interface selection

A URL is required to support multiple request interfaces. You can consider using theRequestConditionThe following is the specific implementation class

public class ApiCondition implements RequestCondition<ApiCondition> {

    private ApiItem version;

    public ApiCondition(ApiItem version) {
        this.version = version;
    }

    @Override
    public ApiCondition combine(ApiCondition other) {
        //Select the interface with the largest version
        return version.compareTo(other.version) >= 0 ? new ApiCondition(version) : new ApiCondition(other.version);
    }

    @Override
    public ApiCondition getMatchingCondition(HttpServletRequest request) {
        String version = request.getHeader("x-api");
        ApiItem item = ApiConverter.convert(version);
        //Get all interfaces less than or equal to version
        if (item.compareTo(this.version) >= 0) {
            return this;
        }

        return null;
    }

    @Override
    public int compareTo(ApiCondition other, HttpServletRequest request) {
        //Get the interface corresponding to the maximum version
        return other.version.compareTo(this.version);
    }
}

Although the above implementation is relatively simple, it is necessary to pay attention to two logics

  • getMatchingConditionMethod controls that only the version of apicondition whose version is less than or equal to the version in the request parameter can satisfy the rule
  • compareToWhen more than oneApiCoonditionWhen this request is satisfied, select the maximum version

customRequestMappingHandlerMappingImplementation classApiHandlerMapping

public class ApiHandlerMapping extends RequestMappingHandlerMapping {
    @Override
    protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) {
        return buildFrom(AnnotationUtils.findAnnotation(handlerType, Api.class));
    }

    @Override
    protected RequestCondition<?> getCustomMethodCondition(Method method) {
        return buildFrom(AnnotationUtils.findAnnotation(method, Api.class));
    }

    private ApiCondition buildFrom(Api platform) {
        return platform == null ? new ApiCondition(new ApiItem()) :
                new ApiCondition(ApiConverter.convert(platform.value()));
    }
}

register

@Configuration
public class ApiAutoConfiguration implements WebMvcRegistrations {

    @Override
    public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
        return new ApiHandlerMapping();
    }
}

Based on this, a micro framework for interface version management has been completed; next, enter the test phase

3. Testing

Case 1. Add version on method

Design three interfaces, one without annotation, two with different versions of annotation

@RestController
@RequestMapping(path = "v1")
public class V1Rest {

    @GetMapping(path = "show")
    public String show1() {
        return "v1/show 1.0.0";
    }

    @Api("1.1.2")
    @GetMapping(path = "show")
    public String show2() {
        return "v1/show 1.1.2";
    }

    @Api("1.1.0")
    @GetMapping(path = "show")
    public String show3() {
        return "v1/show 1.1.0";
    }
}

When the request is initiated, the corresponding response is tested without the version and with the specified version

Implementation of interface version support for back end in spring boot application

  • As can be seen from the screenshot above, if there is no version in the request header, a1.0.0Version of
  • In response, which interface is the largest in version less than the requested version

Case 2. Class version + method version

It’s a bit painful to add a version to each method. In the above annotation definition, class annotation is supported. From the implementation point of view, when there are annotations on both methods and classes, select the largest version

@Api("2.0.0")
@RestController
@RequestMapping(path = "v2")
public class V2Rest {

    @Api("1.1.0")
    @GetMapping(path = "show")
    public String show0() {
        return "v2/show0 1.1.0";
    }

    @GetMapping(path = "show")
    public String show1() {
        return "v2/show1 2.0.0";
    }

    @Api("2.1.1")
    @GetMapping(path = "show")
    public String show2() {
        return "v2/show2 2.1.1";
    }

    @Api("2.2.0")
    @GetMapping(path = "show")
    public String show3() {
        return "v2/show3 2.2.0";
    }
}

According to our implementation rules, both Show0 and show1 will be corresponding<2.1.1There will be conflicts in the version request of;

Implementation of interface version support for back end in spring boot application

  • From the screenshot above, we can see that the request with version less than 2.0.0 reported 404 error
  • If the request version is less than 2.1.1, a conflict exception is reported

4. Others

0. Project & related blog

  • Project: https://github.com/liuyueyi/spring-boot-demo
  • Source: https://github.com/liuyueyi/spring-boot-demo/blob/master/spring-case/201-web-api-version

Related articles

  • Spring boot series of tutorials: custom request matching conditionrequestcondition

1. A grey blog

The best letter is not as good as the above. It’s just a one-of-a-kind remark. Due to the limited personal ability, there are inevitably omissions and mistakes. If you find a bug or have better suggestions, you are welcome to criticize and correct. Thank you very much

Here is a grey personal blog, recording all the blogs in study and work. Welcome to visit

  • One gray blog https://blog.hhui.top
  • Http://spring.hhui.top

Implementation of interface version support for back end in spring boot application

Recommended Today

The use of springboot Ajax

Ajax overview What is Ajax? data Ajax application scenarios? project Commodity system. Evaluation system. Map system. ….. Ajax can only send and retrieve the necessary data to the server, and use JavaScript to process the response from the server on the client side. data But Ajax technology also has disadvantages, the biggest disadvantage is that […]