← 返回首页
🔌

API版本控制

📂 architecture ⏱ 2 min 225 words

API版本控制

URL路径版本

最直观的版本控制方式,通过URL路径区分不同版本。

@RestController
@RequestMapping("/api/v1/users")
public class UserV1Controller {

    @GetMapping("/{id}")
    public UserV1DTO getUser(@PathVariable Long id) {
        return userService.findByIdV1(id);
    }
}

@RestController
@RequestMapping("/api/v2/users")
public class UserV2Controller {

    @GetMapping("/{id}")
    public UserV2DTO getUser(@PathVariable Long id) {
        return userService.findByIdV2(id);
    }
}

Header版本

通过自定义Header传递版本信息,保持URL不变。

@RestController
@RequestMapping("/api/users")
public class UserVersionedController {

    @GetMapping(value = "/{id}", headers = "X-API-Version=1")
    public UserV1DTO getUserV1(@PathVariable Long id) {
        return userService.findByIdV1(id);
    }

    @GetMapping(value = "/{id}", headers = "X-API-Version=2")
    public UserV2DTO getUserV2(@PathVariable Long id) {
        return userService.findByIdV2(id);
    }
}

Query参数版本

通过查询参数指定API版本,灵活但不够优雅。

@RestController
@RequestMapping("/api/users")
public class UserQueryVersionController {

    @GetMapping("/{id}")
    public ResponseEntity<?> getUser(
            @PathVariable Long id,
            @RequestParam(name = "version", defaultValue = "1") int version) {
        return switch (version) {
            case 2 -> ResponseEntity.ok(userService.findByIdV2(id));
            default -> ResponseEntity.ok(userService.findByIdV1(id));
        };
    }
}

版本协商

客户端和服务端协商确定使用的API版本。

@Component
public class ApiVersionNegotiator {

    public int negotiateVersion(HttpServletRequest request) {
        // 1. 检查Header
        String headerVersion = request.getHeader("X-API-Version");
        if (headerVersion != null) {
            return Integer.parseInt(headerVersion);
        }

        // 2. 检查Accept头
        String accept = request.getHeader("Accept");
        if (accept != null && accept.contains("application/vnd.myapp.v")) {
            int start = accept.indexOf("v") + 1;
            int end = accept.indexOf("+", start);
            return Integer.parseInt(accept.substring(start, end));
        }

        // 3. 默认版本
        return 1;
    }
}

版本演进策略

渐进式废弃旧版本,引导客户端升级。

@Component
public class VersionDeprecationFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) throws Exception {
        String path = request.getRequestURI();
        if (path.startsWith("/api/v1/")) {
            response.setHeader("Deprecation", "true");
            response.setHeader("Sunset", "Sat, 01 Jul 2025 00:00:00 GMT");
            response.setHeader("Link", "</api/v2>; rel=\"successor-version\"");
        }
        filterChain.doFilter(request, response);
    }
}