问题
API接口其实是不易用的,所以我们有了PostMan之类的工具去辅助我们调用,同时,它也是不清晰的,所以我们需要有文档或者API Mock之类的工具。但是我们将文档和接口分开本身就是一件不幸的事情,他们一旦分开,很难保持同步,这也是问题的源泉。再次,开发者不去回顾代码逻辑,他自己都无法清楚自己的程序到底需要什么参数,每个参数应该会被用户怎么使用。
从另外一个角度来看,接口属于典型的“写”少“读”多的。也就是你变动你的代码是少数,而用户使用它是多数。所以如何让一个接口自解释,易于使用是非常重要的。首先要对人友好,人学会了,才能将其应用于其他程序里。
一切输入都可以抽象为表单,一个漂亮易用,尽量避免输入的表单就是对人友好的。而一个接口的输出,都可以理解为是表格。一个好的易用的返回结果,应该是二维平铺的,现实世界有大量的程序(也包括人)对它更易于接受。复杂的嵌套徒增理解障碍和引起别人的错误以外,似乎没有什么额外的用处。
表单 -> 你的系统 -> 表格
解决方案
要能让接口自动生成一个易于使用的表单,从而简化人们对接口的使用和了解,必须修改接口的开发规则。我们认为,一个接口应该由两部分组成。
- 参数描述,接口描述
- 具体的业务逻辑
Scala 将class 和 object进行了分离。我认为也非常适合API。我们将一个API的参数等描述信息放到Object里,然后业务逻辑放到class里。 比如,我有一个创建脚本文件的接口,对应的描述信息如下:
object CreateScriptFileAction {
object Params {
val USER_ID = Input("userId", "")
val PARENT_ID = Dynamic(
name = "parentId",
subTpe = "TreeSelect",
depends = List(Params.USER_ID.name),
valueProviderName = CreateScriptFileAction_Params_PARENT_ID.action)
val IS_DIR = Select("isDir", List(), valueProvider = Option(() => {
List(
KV(Option("Directory"), Option(ScriptFile.DIR.toString)),
KV(Option("File"), Option(ScriptFile.FILE.toString))
)
}))
val CONTENT = Input("content", "")
val LABEL = Input("label", "")
}
def action = "createScriptFile"
def plugin = PluginItem(CreateScriptFileAction.action,
classOf[CreateScriptFileAction].getName, PluginType.action, None)
}
这里,通过Params我们描述了这个接口需要的所有参数。其中Dynamic参数表示,该参数需要用户先填写USER_ID才会自动触发生成。是属于一个联动的表单组件。
根据前面的描述,前端会自动生成如下表单:
用户填写完userId后,自动多了一个栏目:
接着在Class里完成业务逻辑,比如这里的逻辑比较简单,就是获取userId然后再输出。
class CreateScriptFileAction extends ActionWithHelp {
override def _run(params: Map[String, String]): String = {
JSONTool.toJsonStr(List(Map("userId" -> params(CreateScriptFileAction.Params.USER_ID.name))))
}
override def _help(): String = JSONTool.toJsonStr(FormParams.toForm(CreateScriptFileAction.Params).toList.reverse)
}
_run 是业务逻辑。 _help 则是方便你控制表单的生成。
前面我们看到,通过简单地描述,我们可以生成很好的表单。但是表单里核心难点是,表单的元素存在依赖。比如A选择框依赖于B输入框。用户在B输入了,A才能拿到数据。极端点,用户输入了一个用户id,表单其他所有的选项会自动得到填充。
这个,我们通过Dynamic 类型可以得到很好的解决。解决办法很简答,Dynamic描述了该字段依赖于哪个字段,并且依赖的字段一旦发生变更,应该到哪去获取数据。
如何构建向导
我们知道,单个接口并不能完成一个用户的诉求。通常,要完成一个诉求,通常需要多个接口共同工作,通常这些接口是需要顺序关系的。通过向导,我们可以将完成特定诉求的接口们组织起来。现在,我们看如何完成这么一件事情。
任何使用web-platform 开发的项目,只要安装一个特定的插件,就自动具备表单功能。首先我们看首页:
这是当前应用的所有的接口列表。我们点击第一个Go to nav page,它是一个特殊的功能。
我们输入用户,选择我们需要的向导:
输入用户1,然后自动会提示所有已经创建好的向导,这里我们选择【创建导航】,这里会有一个向导帮助我们做事情。
这里我们,创建一个新的导航,需要三个步骤。我们只要按步骤走,就可以创建一个新的向导。但是我们也看到问题,如果导航项有非常多该怎么办?我们可以单独通过接口[createAPINavItem]为一个已经存在的向导不断添加新的步骤:
大部分参数都会有自动提示。这意味着,我们可以利用已有的接口去创建新的向导,从而能够帮助用户做更复杂的事情。