因为文档已经在 github 里写好了, 这里并非很想再写一次中文版文档, 本文将着重于解析工具的设计.( 好像看国人写的英文也是件蛮痛苦的事哇? 本人英文渣渣. )javascript
实际上 Retrofit 是 Java 的一款基于 OkHttp 开发的, 类型安全的声明式HTTP客户端, 在 Java 用惯了这一类的工具, 天然也会想在其余地方使用相似的工具, 因而乎本身写了一款, 加个 Js 看成后缀就当项目名了, 即 RetrofitJs.前端
固然主要仍是由于懒, 假如在项目的每一个页面都要写一大串 client.get({ ...config })
来发起请求, 一来感受太乱, 毕竟每一个页面都要写就感受很难管理. 二来重复度高, 由于同一个接口调用两次就要写两次配置( 说实话直接封装成函数我也以为有点拙 ), 接口并不能重用. 本着懒惰是第一辈子产力的原则就写了个工具来解决上述问题.java
Talk is cheap, 先来看个 demo, 这是 TypeScript 下的代码:node
// This is typescript demo, also javascript demo( it is if you remove all type define )
// In the first step, you must create your retrofit object.
let client = Retrofit.getBuilder()
.setConfig<RetrofitConfig>( { /** config, you can use retrofit config or axios config */ } )
.addInterceptor( /** your interceptor */ )
.setErrorHandler( /** define your error handler */ )
.build();
// This is the part of define any interface what you need.
@HTTP( "/testing" )
@Headers( [ "Cache-Control: no-store" ] )
class TestingClient {
@GET( "/demo1/:callByWho/:when" )
public demo1( @Path( "callByWho" ) name: string, @Path( "when" ) time: number ): RetrofitPromise<string> & void {
}
@POST( "/demo2/:file" )
public demo2( @Path( "file" ) file: string, @Header( "cookie" ) val: string, @Config localConfig: AxiosConfig ): RetrofitPromise<string> & void {
}
}
// The final step, create your client.
export let testingClient = client.create( TestingClient );
// When you are calling this method, it is a http call actually.
testingClient.demo1( "itfinally", Date.now() ).then( response => {
// any code
} ).catch( reason => {
// any code
} );
// And you can also get axios instance.
let axios: AxiosInstance = client.getEngine();
复制代码
上面的例子其实还少了个东西, 实际上接口是能够继承的. 例如这样:ios
class Parent {
@GET( "/a1_testing/:a1" )
public demo1(): RetrofitPromise<string> & void {
}
}
@HTTP( "/test" )
class TestingClient extends Parent {
}
复制代码
基于 Axios 的固然是同时支持 browser/nodejs 啦.git
不过须要注意的是, 项目核心功能依赖于 Proxy
, 因此并不能在非原生支持 ES6 的环境下使用. 同时 decorator 特性仍然在 stage 2, 而且最重要的 parameter decorator 在 17 年尾才被人提出并加入讨论( 这些事件我都有在项目文档开头说明, 须要了解的能够移步到项目的README.md ), 因此这个工具目前只能在 TypeScript 环境中使用.( 不要怪我等你脱了裤才告诉你这个事 = =. )github
在设计前其实看过 Axios 和 OkHttp 的使用文档, 固然是更偏向于 OkHttp 的方式, 尤为是拦截器方面的设计.typescript
// Axios 使用方式
// Add a request interceptor
axios.interceptors.request.use(function (config) {
// Do something before request is sent
return config;
}, function (error) {
// Do something with request error
return Promise.reject(error);
});
// Add a response interceptor
axios.interceptors.response.use(function (response) {
// Do something with response data
return response;
}, function (error) {
// Do something with response error
return Promise.reject(error);
});
// OkHttp 使用方式
class MyInterceptor implements Interceptor {
public Response intercept(Interceptor.Chain chain) throws IOException {
chain.proceed( chain.request() );
}
}
复制代码
明显是 OkHttp 在同一个做用域中处理一切的方式优于 Axios 把 request/response 隔离成两个做用域的方式, 固然我没详细看过 Axios 源码, 这里不予过多评论, 说不定别人是有缘由的.npm
无论怎样, 当时是查了不少关于 OkHttp 源码解析的文档和博客, 其实关键就是实现一个责任链, 而且每一个节点均可以把 request 传递给下一个节点.axios
因而就有了以下代码:
这是 InterceptorChainActor 里 Chain
的一段代码, 也是整个工具运行的其中一个关键, 字段 interceptors
是当前剩余的拦截器, 而字段 stack
是当前的调用栈. 能够从图片中看出整个责任链是经过 interceptors-stack 二者来维护的, 同时把自身( 即 Chain
)做为参数传入当前调用的拦截器, 从而确保拦截器在调用 chain.proceed( chain.request() )
时, 调用流程会重入到当前的 Chain
.
要注意的是, 这其实是一个递归调用, 因此拦截器太多的话也会出现溢出, 固然这是极端状况了.
另外在调用一个被 RetrofitJs 代理的方法时, 实际的调用以下:
为啥不截图呢? 由于整个代码太多了, 好几份文件.
说实话, 这个工具相对于前端而言, 特性有点激进, 固然也解决一部分问题, 最直观的就是代码更简洁, 尤为是调用的时候给人一种本地函数调用的错觉( 有点像RPC? ).
都看到这里了, 不介意的话能够试试哇, 目前项目在 npm 提供.