协和医院网站建设目标,网站策划主要做什么工作,游戏网站 模板,京东电子商务网站的建设实战#xff1a;实现Web API版本控制
前面介绍了Spring Boot如何构建RESTful风格的Web应用接口以及使用Swagger生成API的接口文档。如果业务需求变更#xff0c;Web API功能发生变化时应该如何处理呢#xff1f;可以通过Web API的版本控制来处理。
1.为什么进行版本控制
…实战实现Web API版本控制
前面介绍了Spring Boot如何构建RESTful风格的Web应用接口以及使用Swagger生成API的接口文档。如果业务需求变更Web API功能发生变化时应该如何处理呢可以通过Web API的版本控制来处理。
1.为什么进行版本控制
一般来说Web API是提供给其他系统或其他公司使用的不能随意频繁地变更。然而由于需求和业务不断变化Web API也会随之不断修改。如果直接对原来的接口修改势必会影响其他系统的正常运行。
例如系统中用户添加的接口/api/user由于业务需求的变化接口的字段属性也发生了变化而且可能与之前的功能不兼容。为了保证原有的接口调用方不受影响只能重新定义一个新的接口/api/user2这使得接口维护难度增加。
如何做到在不影响现有调用方的情况下优雅地更新接口的功能呢
最简单高效的办法就是对Web API进行有效的版本控制。通过增加版本号来区分对应的版本来满足各个接口调用方的需求。版本号的使用有以下几种方式
1通过域名进行区分即不同的版本使用不同的域名如v1.api.test.com、v2.api.test.com。
2通过请求URL路径进行区分在同一个域名下使用不同的URL路径如test.com/api/v1/、test.com/api/v2。
3通过请求参数进行区分在同一个URL路径下增加versionv1或v2等然后根据不同的版本选择执行不同的方法。
在实际项目开发中一般选择第二种方式因为这样既能保证水平扩展又不影响以前的老版本。
2.Web API的版本控制
Spring Boot对RESTful的支持非常全面因而实现RESTful API非常简单同样对于API版本控制也有相应的实现方案
1创建自定义的APIVersion注解。
2自定义URL匹配规则ApiVersionCondition。
3使用RequestMappingHandlerMapping创建自定义的映射处理程序根据Request参数匹配符合条件的处理程序。
下面通过示例程序来演示Web API如何增加版本号。
步骤01 创建自定义注解。
创建一个自定义版本号标记注解ApiVersion。实现代码如下
Target({ElementType.TYPE})Retention(RetentionPolicy.RUNTIME)public interface ApiVersion {/*** return版本号*/int value() default 1;}在上面的示例中创建了ApiVersion自定义注解用于API版本控制并返回了对应的版本号。
步骤02 自定义URL匹配逻辑。
接下来定义URL匹配逻辑创建ApiVersionCondition类并继承RequestCondition接口其作用是进行版本号筛选将提取请求URL中的版本号与注解上定义的版本号进行对比以此来判断某个请求应落在哪个控制器上。实现代码如下
public class ApiVersionCondition implements RequestConditionApiVersionCondition {private final static Pattern VERSION_PREFIX_PATTERN Pattern.compile(.*v(\\d).*);private int apiVersion;ApiVersionCondition(int apiVersion) {this.apiVersion apiVersion;}private int getApiVersion() {return apiVersion;}Overridepublic ApiVersionCondition combine(ApiVersionCondition apiVersionCondition) {return new ApiVersionCondition(apiVersionCondition.getApiVersion());}Overridepublic ApiVersionCondition getMatchingCondition(HttpServletRequest httpServletRequest) {Matcher m VERSION_PREFIX_PATTERN.matcher(httpServletRequest.getRequestURI());if (m.find()) {Integer version Integer.valueOf(m.group(1));if (versionthis.apiVersion) {return this;}}return null;}Overridepublic int compareTo(ApiVersionCondition apiVersionCondition, HttpServletRequest httpServletRequest) {return apiVersionCondition.getApiVersion() - this.apiVersion;}
}在上面的示例中通过ApiVersionCondition类重写RequestCondition定义URL匹配逻辑。
当方法级别和类级别都有ApiVersion注解时通过ApiVersionRequestCondition.combine方法将二者进行合并。最终将提取请求URL中的版本号与注解上定义的版本号进行对比判断URL是否符合版本要求。
步骤03 自定义匹配的处理程序。
接下来实现自定义匹配的处理程序。先创建ApiRequestMappingHandlerMapping类重写部分RequestMappingHandlerMapping的方法实现自定义的匹配处理程序。示例代码如下
public class ApiRequestMappingHandlerMapping extends RequestMappingHandlerMapping {private static final String VERSION_FLAG {version};private static RequestConditionApiVersionCondition createCondition(Class? clazz) {RequestMapping classRequestMapping clazz.getAnnotation(RequestMapping.class);if (classRequestMapping null) {return null;}StringBuilder mappingUrlBuilder new StringBuilder();if (classRequestMapping.value().length 0) {mappingUrlBuilder.append(classRequestMapping.value()[0]);}String mappingUrl mappingUrlBuilder.toString();if (!mappingUrl.contains(VERSION_FLAG)) {return null;}ApiVersion apiVersion clazz.getAnnotation(ApiVersion.class);return apiVersion null ? new ApiVersionCondition(1) : new ApiVersionCondition(apiVersion.value());}Overrideprotected RequestCondition? getCustomMethodCondition(Method method) {return createCondition(method.getClass());}Overrideprotected RequestCondition? getCustomTypeCondition(Class? handerType) {return createCondition(handerType);}
}
步骤04 配置注册自定义的RequestMappingHandlerMapping。
创建WebMvcRegistrationsConfig类重写getRequestMappingHandlerMapping( )的方法将之前创建的ApiRequestMappingHandlerMapping注册到系统中。
public class WebMvcRegistrationsConfig implements WebMvcRegistrations {Overridepublic RequestMappingHandlerMapping getRequestMappingHandlerMapping() {return new ApiRequestMappingHandlerMapping();}
}通过以上4步完成API版本控制的配置。代码看起来复杂其实都是重写Spring Boot内部的处理流程。
步骤05 配置实现接口。
配置完成之后接下来编写测试的控制器Controller实现相关接口的测试。在Controller目录下分别创建OrderV1Controller和OrderV2Controller。示例代码如下
//V1 版本的接口定义
ApiVersion(value 1)
RestController
RequestMapping(/api/{version}/order)
public class OrderV1Controller {GetMapping(/delete/{orderId})public JSONResult deleteOrderById(PathVariable String orderId) {System.out.println(V1删除订单成功 orderId);return JSONResult.ok(V1删除订单成功);}GetMapping(/detail/{orderId})public JSONResult deleteOrderById(PathVariable String orderId) {System.out.println(V1获取订单详情成功 orderId);return JSONResult.ok(V1获取订单详情成功);}
}//V2 版本的接口定义
ApiVersion(value 2)
RestController
RequestMapping(/api/{version}/order)
public class OrderV2Controller {GetMapping(/delete/{orderId})public JSONResult deleteOrderById(PathVariable String orderId) {System.out.println(V2获取订单详情成功 orderId);return JSONResult.ok(V2获取订单详情成功);}GetMapping(/list)public JSONResult list() {System.out.println(V2,获取list订单列表接口);return JSONResult.ok(200, V2,新增list订单列表接口);}
}在上面的示例中我们在UserV1Controller中定义了/delete/{orderId}和/detail/{orderId}两个接口在UserV2Controller中修改/detail/{orderId}接口新增/list接口然后使用ApiVersion自定义注解设置两个Controller的版本号。
步骤06 验证测试。
配置完成之后启动项目查看版本控制是否生效。在浏览器中分别访问api/v1/order/delete/20011和api/v2/order/ delete/20011订单删除接口查看页面返回情况。如下图所示调用V1和V2版本的order/ delete/20011订单删除接口返回的都是“V1删除订单成功”。这说明V2会默认继承V1的所有接口新版本的原有接口功能保持不变。 接下来在浏览器中分别访问api/v1/order/detail/20011和api/v2/order/delete/20011订单详情接口查看页面返回情况。如下图所示分别调用V1和V2版本的order/detail/20011订单详情接口返回的是各自版本的接口信息说明V2版本对order/detail订单详情接口的修改生效同时也没有影响旧版本的订单详情接口。 最后分别访问新增的order/list订单列表接口查页面返回情况。如下图所示请求V1的order/list订单列表返回404接口不存在请求V2的order/list订单列表返回正确的结果说明在高版本中新增的接口只在高版本中生效。 以上验证情况说明Web API的版本控制配置成功实现了旧版本的稳定和新版本的更新。
1当请求正确的版本地址时会自动匹配版本的对应接口。
2当请求的版本大于当前版本时默认匹配最新的版本。
3高版本会默认继承低版本的所有接口。实现版本升级只关注变化的部分没有变化的部分会自动平滑升级这就是所谓的版本继承。
4高版本的接口的新增和修改不会影响低版本。
这些特性使得在升级接口时原有接口不受影响只关注变化的部分没有变化的部分自动平滑升级。这样使得Web API更加简洁这就是实现Web API版本控制的意义所在。