我想在我的 RESTful Web 服务 API 中实现版本控制。我打算将版本放入 URL,即: /api/v3/endpoint
执行此操作的理想方法是什么(在 Java 中)?
尽管这激怒了我对手动版本控制的厌恶,但我最好的猜测是将 API 接口(interface)保存到一个新文件中,并包含一堆注释以防止过多的熵:
/** Do not leave more than 2 previous versions in existence! **/
@Path("/api/v3")
public interface RestfulAPIv3
{
int version = 3;
@Path("/resources")
@Method(GET)
public Response getResources();
}
当然,我们的想法是不也复制实现,而是允许它支持多个版本。这可能需要将相同的签名移至较新的版本,这样类文件中的接口(interface)之间就不会发生冲突:
public class RestfulAPIImpl implements RestfulAPIv3, RestfulAPIv2
{
public Response getResources()
{
List<Resource> rs = HibernateHelper.getAll(Resource.class);
// Can we do something with v2/v3 diffs here?
}
@Deprecated
public Response getOptions() // ONLY in v2!
{
return HibernateHelper.getOptions();
}
}
仔细想想,我不知道我们如何知道客户端调用了哪个版本的端点,除非将请求转发到方法中,这不是我最喜欢的事情。
所以,我的问题是 - 所有版本化 API 实现者都在做什么来防止所有这些事情失控?最好的方法是什么?我走在正确的道路上吗?
(注意:this other question 是关于“如果” - 我的问题是关于“如何”。)
最佳答案
不传递指定版本号的参数的另一种方法是向方法添加注释,以便它自动捕获该信息并将其保存在可以在其他地方读取的请求对象上。
考虑到您的 API 可能会发出带有不同版本参数的请求,并且响应看起来也会有所不同,您可能必须拥有多个 Controller 和 View 模型类,每个 API 版本对应一个 Controller 和 View 模型类。
更新
根据请求,遵循一些示例代码(我使用了 Play Framework 2.4)。
所以目标是在 Controller 类中实现类似的功能:
@Versioned(version = 0.1)
public Result postUsers() {
// get post data
UsersService service = new UsersService(getContext());
service.postUsers(postData);
// return result
}
在服务类中像这样:
public class UsersService extends Service {
public UsersService(RequestContext context) {
super(context);
}
public ReturnType postUsers() {
double apiVersion = getContext().getAPIVersion();
// business logic
}
}
为了实现这一点,您需要一个版本化注释:
import java.lang.annotation.*;
import play.mvc.With;
@With(VersioningController.class)
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Versioned {
double version();
}
还有 VersioningController:
import play.libs.F.Promise;
import play.mvc.*;
public class VersioningController extends Action<Versioned> {
public final static String API_VERSION = "version";
@Override
public Promise<Result> call(Http.Context context) throws Throwable {
context.args.put(API_VERSION, configuration.version());
return delegate.call(context);
}
}
还有一个 RequestContext 来帮助您管理它(您还可以使用请求上下文来管理请求时间戳、请求操作的用户等):
public class RequestContext {
private double version;
public RequestContext(Double version) {
setAPIVersion(version);
}
public double getAPIVersion() {
return version;
}
public void setAPIVersion(double version) {
this.version = version;
}
}
此外,您的 Controller 可以有一个 GenericController,它们都可以从中扩展:
import play.api.Play;
import play.mvc.*;
import play.mvc.Http.Request;
public abstract class GenericController extends Controller {
protected static RequestContext getContext() {
return new RequestContext(getAPIVersion());
}
protected static double getAPIVersion() {
return (double) Http.Context.current().args
.get(VersioningController.API_VERSION);
}
}
以及一个抽象服务,所有服务类都从中扩展:
public abstract class Service {
private RequestContext context;
public Service(RequestContext context) {
setContext(context);
}
public RequestContext getContext() {
return context;
}
public void setContext(RequestContext context) {
this.context = context;
}
}
综上所述,请记住,最好尝试在尽可能少的层中隔离 API 版本控制。如果您可以让业务逻辑类不必管理 API 版本,那就更好了。
关于java - 如何维护 Java 中的 RESTful API 版本?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33041979/