background

When I was making the back-end API interface, I was teased by the front-end colleagues that the previous INTERFACE URL did not follow the Restful API specification, so I had a meeting to discuss and finally decided that the new interface definition should follow the standard specification.

Looked about Restful API, or easy to understand the bosses said: www.ruanyifeng.com/blog/2014/0…

It says the version number of the API.

After consultation with the team, the URL should also highlight the version number. Therefore, the API developed in a version phase should specify the version number of the URL.

In the actual code, the following problems were found:

If words like “v1” were written directly into the controller mapping, it would look something like this:

@GetMapping("/v1/user/info")
Copy the code

It’s not flexible enough to write “v1” again for every actual URL.

Is there a way to directly concatenate all v1 in front of the url that you specify?

So here’s the question: How does Spring Boot change the request URL?

Problem description

How does Spring Boot change the request URL?

Keywords: Spring Boot add URL prefix

The solution

Custom annotation @apiVersion, set annotation modifier method, add prefix value to request mapping.

The value is passed directly to the annotation.

The implementation code

Custom annotation @apiVersion

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiVersion {
    int[] value();
}
Copy the code

The configuration annotation @apiVersion takes effect

A custom beanApiVersionRequestMappingHandlerMapping, the rewriteRequestMappingHandlerMapping


public class ApiVersionRequestMappingHandlerMapping extends RequestMappingHandlerMapping {

    private final String prefix;

    public ApiVersionRequestMappingHandlerMapping(String prefix) {
        this.prefix = prefix;
    }

    @Override
    protected RequestMappingInfo getMappingForMethod(Method method, Class
        handlerType) {
        RequestMappingInfo info = super.getMappingForMethod(method, handlerType);
        if(info == null) return null;

        ApiVersion methodAnnotation = AnnotationUtils.findAnnotation(method, ApiVersion.class);
        if(methodAnnotation ! =null) { RequestCondition<? > methodCondition = getCustomMethodCondition(method);// Concatenate our ApiVersion with the usual request mapping
            info = createApiVersionInfo(methodAnnotation, methodCondition).combine(info);
        } else {
            ApiVersion typeAnnotation = AnnotationUtils.findAnnotation(handlerType, ApiVersion.class);
            if(typeAnnotation ! =null) { RequestCondition<? > typeCondition = getCustomTypeCondition(handlerType);// Concatenate our ApiVersion with the usual request mappinginfo = createApiVersionInfo(typeAnnotation, typeCondition).combine(info); }}return info;
    }

    private RequestMappingInfo createApiVersionInfo(ApiVersion annotation, RequestCondition
        customCondition) {
        int[] values = annotation.value();
        String[] patterns = new String[values.length];
        for(int i=0; i<values.length; i++) {
            // Build the URL prefix
            patterns[i] = prefix+values[i];
        }

        return new RequestMappingInfo(
                new PatternsRequestCondition(patterns, getUrlPathHelper(), getPathMatcher(), useSuffixPatternMatch(), useTrailingSlashMatch(), getFileExtensions()),
                new RequestMethodsRequestCondition(),
                new ParamsRequestCondition(),
                new HeadersRequestCondition(),
                new ConsumesRequestCondition(),
                newProducesRequestCondition(), customCondition); }}Copy the code

Add the configurationWebMvcConfig, the bean is injected at startupApiVersionRequestMappingHandlerMapping

@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {

    @Override
    public RequestMappingHandlerMapping requestMappingHandlerMapping(a) {
        return new ApiVersionRequestMappingHandlerMapping("v");
    }

    @Bean
    protected RequestMappingHandlerMapping customRequestMappingHandlerMapping(a) {
        return new ApiVersionRequestMappingHandlerMapping("v"); }}Copy the code

The Controller method uses annotations

As follows:

The original request URL was: /user/info

With the annotation @apiVersion (1), the request URL becomes /v1/user/info;

Similarly, @apiVersion (2) changes the request URL to /v2/user/info.

@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {

    @Autowired
    private UserService userService;
 
    @GetMapping("/info")
    @ApiVersion(1)
    public R getUserInfo(UserQueryDTO userQueryDTO){
        try {
            return userService.getUserInfo(userQueryDTO)
                    .msg("success");
        } catch (Exception e) {
            log.error("Get user info fail: {}", e);
            return R.fail().msg("get user info fail"); }}}Copy the code