Java 涉及许多可选参数的API库的最佳实践

Java 涉及许多可选参数的API库的最佳实践,java,api-design,canvas-lms,Java,Api Design,Canvas Lms,我们正试图编写一个JavaAPI库来包装Canvas LMS RESTAPI。对于API处理的每种类型的对象,我们都有一个读写器接口。例如,我们有一个UserReader接口,它将从“”端点以及“”端点返回用户对象 我们正在努力解决的问题是如何最好地实现API调用的所有可选参数。如果您查看两个链接端点的规范,您将看到,虽然它们都返回用户对象,但它们采用的参数非常不同。最初的实现只是向listUsersInCourse方法添加了一组列表(可以是空的)和可选参数(可以是空的)。因此,如果您不想指定任

我们正试图编写一个JavaAPI库来包装Canvas LMS RESTAPI。对于API处理的每种类型的对象,我们都有一个读写器接口。例如,我们有一个
UserReader
接口,它将从“”端点以及“”端点返回用户对象

我们正在努力解决的问题是如何最好地实现API调用的所有可选参数。如果您查看两个链接端点的规范,您将看到,虽然它们都返回用户对象,但它们采用的参数非常不同。最初的实现只是向listUsersInCourse方法添加了一组列表(可以是空的)和可选参数(可以是空的)。因此,如果您不想指定任何可选参数,那么对该方法的调用如下所示。是的,你可以有一个不指定任何选项的方法的无参数方便版本,但是只要你想使用任何选项,你就必须处理它们

List<User> userList = userReader.listUsersInCourse("courseId", 
    Collections.emptyList(), Optional.empty(), Collections.emptyList());
这将大大减少我们编写和维护代码的工作量,但会迫使库的用户了解更多,并传入Canvas API中定义的神奇字符串。如上文第1节所述,减少结构化的最大好处是,当Canvas API添加新选项时,我们的用户可以立即使用它,而不必等待包含新方法的库的新版本

4. 还有一个选项也可以在每个API方法中接受一个完全通用的选项列表,并强制库的用户更多地参与构建请求。这看起来像这样:

List<Pair> optsList = new ArrayList<>();
optsList.add(new ImmutablePair("search_term", "Myname"));
optsList.add(new ImmutablePair("enrollment_type[]", EnrollmentType.Student.name());
optsList.add(new ImmutablePair("enrollment_type[]", "EnrollmentType.Teacher.name());
List<User> userList = userReader.listUsersInCourse("courseId", optsList);
List optsList=newarraylist();
添加(新的ImmutablePair(“搜索词”、“我的名字”);
添加(新的ImmutablePair(“注册类型[]”,注册类型.Student.name());
添加(新的ImmutablePair(“注册类型[]”,“注册类型.教师.名称());
List userList=userReader.listUsersInCourse(“courseId”,optsList);

这些是我提出的选项。如果你使用这个API库,你会发现哪一个最有用?你宁愿写/维护哪一个?我完全没有一个更好的方法来做它吗?< /P> < P>如果我对你的理解正确,你的问题是:“我应该做多少工作来帮助我的用户?”"


听起来Canvas API很复杂,而且还在不断变化,这会让我倾向于将负担推到用户身上,比如您的第四个选项。如果某些部分太复杂,用户无法使用,那么仍然有可能实现第二个选项。

您还可以创建一个JAX-RS libr使用客户端代理

定义一个像

@Path("/api")
public interface CanvasAPI {

@GET
@Path("/v1/accounts/{accountId}/courses")
Response getCoursesForAccount(@PathParam("accountId") long accountId,
    @QueryParam("with_enrollments") boolean withEnrollments, @QueryParam("enrollment_term_id") long termId);

}
然后创建一个客户端代理

new ResteasyClientBuilder().build().target("<base_url>").proxy(CanvasAPI.class);
new ResteasyClientBuilder().build().target(“”).proxy(CanvasAPI.class);
这将为您提供一个rest代理类,您可以像普通类一样使用它

CanvasAPI api = new ResteasyClientBuilder().build().target("<base_url>").proxy(CanvasAPI.class);
data = api.getCoursesForAccount(5l, true, 2l); 
CanvasAPI=new ResteasyClientBuilder().build().target(“”).proxy(CanvasAPI.class);
数据=api.getCoursesForAccount(5l,true,2l);

这种方法很容易与api文档保持同步,因为您只需模仿规范中定义的调用,让代理为您构建所有内部工作。

我不确定这是否适合StackOverFlow,它是为了在特定编程问题上寻求帮助。您的问题将有很多意见ed回答说,我认为你应该把它移到前面,这样可能会得到更好的结果。我可能会选择实现选项2和3。我不认为选项2会比选项1更难。@jxh它更难,因为Canvas API有很多端点,所以我们将创建十个,甚至一百个特定于端点的op选项类。下面是API的完整列表:我的意思是,您的API无论如何都应该强制执行有效的参数,即使使用选项1。如果您认为在静态表中编写强制代码以检查通用列表或参数更容易,那么您可以这样做。但是使用选项1或2,我仍然会包括选项3.Ev虽然这有点痛苦,但我同意#2对用户来说是最好的——它利用Java的强大类型帮助用户了解哪些选项可用于哪个调用。这让人想起了[Amazon的AWS Java API][,其中有一个fluent
CreateBucketRequest
类用于
AmazonS3Client::createBucket
方法。作为对您的前沿客户的妥协,我还将包括#3。
@Path("/api")
public interface CanvasAPI {

@GET
@Path("/v1/accounts/{accountId}/courses")
Response getCoursesForAccount(@PathParam("accountId") long accountId,
    @QueryParam("with_enrollments") boolean withEnrollments, @QueryParam("enrollment_term_id") long termId);

}
new ResteasyClientBuilder().build().target("<base_url>").proxy(CanvasAPI.class);
CanvasAPI api = new ResteasyClientBuilder().build().target("<base_url>").proxy(CanvasAPI.class);
data = api.getCoursesForAccount(5l, true, 2l);