自动生成Android,iOS的REST API访问代码
我们正在招聘,需要Android和iOS工程师的加入,和我一组,开发看准网 的App,为四亿职场人服务。 请发简历 到 shenfeng at kanzhun dot com,请加入我们
上个月,介绍了生成Java JDBC访问代码,从SQL文件,后面应用到我负责的几个实际的项目,效果不错, 程序清晰了很多,也更好维护。
用类似的思路,我做了一个新的工具: api-kit,移动开发从中受益。
移动开发:Android,iOS,RESTful API
移动开发,下面几个环节免不了:
- 负责Server端开发的同学,写API,写文档
- 负责Android的同学,根据文档,以及和server端同学的交流,实现和服务器的通信代码
- 负责iOS的同学,根据文档,以及和server端同学的交流,实现和服务器的通信代码
这里面有一些重复: 负责server的同学开发API后,写文档是把同样的信息,表达了两次,一次是在code里面,一次在文档。 客户端同学实现各自平台的访问代码,是重复,因为这部分信息是server端的code(或者文档)的一次翻译(跨语言,跨平台)。
- 信息在这个过程中是无创造的复制(或者翻译,即没有引入更多的信息)
- 不能保证复制的正确性,比如文档错误,客户端写code引入bug,等
- 复制的成本较高:沟通成本,写code的成本
- 如果API重构,这个改动,需要在其它几个地方replay
这几天,我写了一个程序,api-kit,通过自动生成代码的方式,解决这里面的大部分重复,使信息的流动更高效和快捷。
例子
假设API描述是这样的:
// 这是api-kit的输入,输出是各个平台的code:Android, iOS, Server端
struct Book {
i32 id
string title
string isbn
float32 price
string description
}
@url(/books/newest)
@get
func list<Book> getNewest(i32 limit, i32 offset);
@url(/books/search)
@get
func list<Book> searchBook(string q, i32 limit, i32 offset);
API描述是api-kit的输入,输出是各个平台(Server,Android, iOS)的code。
生成的Server端code
- IHandler interface
- Book类
- hook这个interface到servlet的支持代码(参数解析,绑定,dispatch)
public interface IHandler {
// Called before every function. Use cases: setup context, authentication return false to abort further execution.
public boolean before(Context context);
// Called after every function. One use case is logging
public void after(Context context);
// GET /api/books/newest
public List<Book> GetNewest(Context context, int limit, int offset);
// GET /api/books/search
public List<Book> SearchBook(Context context, String q, int limit, int offset);
}
服务器端的工作简化为实现这个Interface
生成的Android端code
生成的code,处理url拼接,返回值解析,暴露给程序员的是函数:
零依赖,能有效的减少apk的体积。在compiler的帮助下,做到类型安全,服务器重构后,这边会编译出错,refactor会方便一些。
生成的iOS的code
iOS显然是需要支持的。先花了一天时间,学习swift,并完成swift的code生成。在和公司的iOS工程师沟通时,他们在用objective-c, 于是又多花了一天时间看oc,并生成oc的code,语法有点不太习惯,但还是搞定了。
也是零依赖的,能有效的减少app的体积。由于oc支持类型安全的dict和arr挺困难,于是,通过注释帮助一点。
Batch,打包多个请求
由于移动的特殊性(latency),一个页面一般需要请求多次,才能render。请求多用异步,多个异步的嵌套挺不方便, 如果能合并这些请求,移动端的开发会更方便,也会提高性能。api-kit透明的实现了这一点。
ns com.kanzhun.api
struct Book {
i32 id
string title
string isbn
float32 price
string description
}
struct NewestReq {
i32 limit
i32 offset
}
struct SearchBookReq {
string q
i32 limit
i32 offset
}
@url(/books/newest)
@get
func list<Book> GetNewest(NewestReq req); // batch要求参数的个数仅为一个
@url(/books/search)
@get
func list<Book> SearchBook(SearchBookReq req);
// `batch` 为关键字
// GetNewest, SearchBook 都是函数名
// batch请求,和服务端是一次交互,客户端发送一次请求给服务端
@url(/batch)
batch Batch(GetNewest, SearchBook)
生成的code
- 在服务器端透明的处理掉Batch请求
- 在客户端生成名为Batch的函数,接受GetNewest的req,SearchBook的req,返回BatchResp
现在的状态
- Android,iOS,服务端(servlet)已经完成,已测试。生成的code和手写的代码一样,高效简洁。
- 除去Batch,没有任何的私有协议。这里采用的是大家熟悉的HTTP,JSON,RESTful。 事实上,api-kit也可以用来生成已有的restful api的客户端code。
- Batch用了一点私有协议: 请求打包,post给服务器,服务器拆开,取出一个一个的请求,挨个调用,打包每个函数的返回值,返回客户端。Batch的服务端逻辑,是自动生成的
- Api的定义文件,是个很好的文档。api-kit把文档,翻译成了可以执行的代码。代码也是文档。这会节约团队之间的交流成本, 节约出来的时间,可以干更有意思的事情,比如晒晒太阳,喝喝咖啡,和奶奶聊聊天,听她讲故事。
- Rest API的url endpoint是什么,对于客户端来说,变成了实现细节。客户端变成仅关心 函数名,参数,返回值,而这些,IDE会给我们很好的帮助。
- 整个流程简化为:定义api规范,服务器端实interface。客户端的代码已经生成好。大家思考的方式,变成函数,返回值,参数。 交流也就变为该调用哪个函数,某个参数是什么意思,参数名,函数名,在common sense的帮助下,不少也是self explained,这部分沟通也会省掉一些。
接下来的工作
- 可能我会想办法做到透明的cache(生成cache的code),
- 支持其它语言,比如生成ajax调用的js code,用于支持网站开发,Python的客户端(开发时的黑盒子测试)
- 生成go的服务器端代码
我们正在招聘,需要Android和iOS工程师的加入,和我一组,开发看准网 的App,为四亿职场人服务。 请发简历 到 shenfeng at kanzhun dot com,请加入我们