SpingMVC框架实现restfull接口的版本控制

互联网发展到今天,基于restfull开发的系统也越来越多,不再局限于jsp等脚本语言来实现动态数据的展示,而是通过后台提供的http接口给前端调用,但是当系统越做越大,同一个接口可能会不断的修改,一旦调用方式发生改变,后果是非常严重的,客户端将无法正常调用,除非强制客户端升级到最新版本,这个也是不太现实的,那怎么办呢?我们想到的就是通过版本来控制同一个接口,类似这样的一个地址:http://localhost:8080/api/v1,其中v1就是当前所用到的版本,那怎么来实现呢?直接上代码:

首先自定义一个注解:

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.web.bind.annotation.Mapping;

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

	/**
	 * 标识版本号
	 * @return
	 */
	int value();
}

这个注解标识当前接口所对应的版本

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import cn.sunsharp.platform.annotation.ApiVersion;
import cn.sunsharp.platform.web.BaseController;


@RequestMapping("{version}")
@RestController
public class HelloController extends BaseController {

	@ApiVersion(1)
	@RequestMapping("hello")
	public String hello1(){
		return "hello1";
	}
	
	@ApiVersion(2)
	@RequestMapping("hello")
	public String hello2(){
		return "hello2";
	}
}

这样我们通过v1,v2这样的地址就能访问到对应的版本,但是这样会有一个问题,如果客户端传入v3,但是所定义的版本并没有定义v3,此时客户端肯定会报404错误,也就是没有这个页面,怎么办呢,相当的就是拦截器,如果传入的版本没有,则默认访问最新的版本,请看代码:

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletRequest;

import org.springframework.web.servlet.mvc.condition.RequestCondition;

public class ApiVersionCondition implements RequestCondition {

	// 路径中版本的前缀, 这里用 /v[1-9]/的形式
    private final static Pattern VERSION_PREFIX_PATTERN = Pattern.compile("v(\\d+)/");
     
    private int apiVersion;
     
    public ApiVersionCondition(int apiVersion){
        this.apiVersion = apiVersion;
    }
     
    public ApiVersionCondition combine(ApiVersionCondition other) {
        // 采用最后定义优先原则,则方法上的定义覆盖类上面的定义
        return new ApiVersionCondition(other.getApiVersion());
    }
 
    public ApiVersionCondition getMatchingCondition(HttpServletRequest request) {
        Matcher m = VERSION_PREFIX_PATTERN.matcher(request.getRequestURI());
        if(m.find()){
            Integer version = Integer.valueOf(m.group(1));
            if(version >= this.apiVersion) // 如果请求的版本号大于配置版本号, 则满足
                return this;
        }
        return null;
    }
 
    public int compareTo(ApiVersionCondition other, HttpServletRequest request) {
        // 优先匹配最新的版本号
        return other.getApiVersion() - this.apiVersion;
    }
 
    public int getApiVersion() {
        return apiVersion;
    }
}

import java.lang.reflect.Method;

import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.servlet.mvc.condition.RequestCondition;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import cn.sunsharp.platform.annotation.ApiVersion;

public class CustomRequestMappingHandlerMapping extends
		RequestMappingHandlerMapping {

	@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);
    }
     
    private RequestCondition createCondition(ApiVersion apiVersion) {
        return apiVersion == null ? null : new ApiVersionCondition(apiVersion.value());
    }
}

这样,当传入的版本没有则默认会访问最新的版本,还没有完,还需要注册这个拦截器:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import cn.sunsharp.platform.version.CustomRequestMappingHandlerMapping;

@Configuration
public class WebConfig extends WebMvcConfigurationSupport {

	@Override
    @Bean
    public RequestMappingHandlerMapping requestMappingHandlerMapping() {
        RequestMappingHandlerMapping handlerMapping = new CustomRequestMappingHandlerMapping();
        handlerMapping.setOrder(0);
        handlerMapping.setInterceptors(getInterceptors());
        return handlerMapping;
    }
}

如此就实现了最基本的对api的版本控制。

你可能感兴趣的:(java)