用于浏览器和 Node.js 的基于 Axios 的声明式 HTTP 客户端。
由一位习惯了声明式 HTTP 客户端的 java 程序员编写。
目标是在所有 javascript/typescript 环境中提供一个简单、简洁的 http 客户端(实际上是 es6 环境) ( 还没有! )
由于babel-plugin-transform-decorators-legacy
尚不支持参数装饰器,因此 RetrofitJs 目前仅适用于 TypeScript 环境。
在此引用该提案:
您可以装饰整个类,以及字段、getter、setter 和方法的声明。参数和函数声明不能被修饰。 -- 来自提案装饰者
也有人提出了这个要求 --> 参数装饰器?
现在,让我们专注于声明自己的http接口而不是http细节,就像Retrofit一样,少写,多做。
最后,非常感谢所有编写或更新 Axios(javascript http 客户端)和 Retrofit(java http 客户端)的人们,这些都是很棒的项目,它教会了我很多东西。
该程序无法在 IE8/9/10/11 和其他原生不支持 es6 的环境(通常是浏览器)上运行,因为它依赖于Proxy
对象( es6 )和Decorator
功能(阶段 2 )。
换句话说,RetrofitJs 可以在任何原生支持 es6 的环境下工作。
关于Proxy
对象,我找不到任何支持的polyFill。但对于Decorator
,你可以通过 babel 支持的东西来使用它,比如babel-plugin-transform-decorators-legacy
。
使用 npm:
npm install retrofitjs
// 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 ( ) ;
和Retrofit一样,RetrofitJs也是通过拦截器链实现的。所有拦截器按照order
字段排序,从大到小排序。
// This is a interceptor interface
export interface Interceptor {
order : number ;
init ( config : RetrofitConfig ) : void ;
intercept ( chain : Chain ) : Promise < ResponseInterface < any > >
}
注意, RealCall
拦截器是最后一个拦截器,而且必须是。
它是一个默认拦截器, order
字段的值为零。 RetrofitJs 使用它通过 Axios 对象发送所有 http 请求。
为了避免冲突,小于256(不包括256)的订单字段编号被保留。
您可以轻松地实现自己的拦截器,就像这样。
class MyInterceptor implement Interceptor {
public order : number = 256 ;
public init ( config : RetrofitConfig ) : void {
// Initializing your interceptor
}
public intercept ( chain : Chain ) : Promise < ResponseInterface < any > > {
// Your code
return chain . proceed ( chain . request ( ) ) ;
}
}
注意chain.proceed( chain.request() )
,此代码决定请求是否继续。
如果使用chain.proceed
调用,当前请求将转移到下一个拦截器。否则其余的拦截器将不会被激活。如果拦截器链抛出任何错误,整个过程将被终止并激活错误处理程序。
其实插件就是拦截器,你可以为RetrofitJs编写自己的插件。
Retrofit.use( new MyInterceptor implements Interceptor {
public order: number = 20;
public init( config: RetrofitConfig ): void {
}
public intercept( chain: Chain ): Promise<ResponseInterface<any>> {
}
} );
当你创建一个实例时,它会将所有拦截器添加到Retrofit
中。
插件的顺序字段没有限制。事实上,这些数字是为插件和默认拦截器保留的。
这是所有默认拦截器:
拦截器 | 命令 |
---|---|
真实通话 | 0 |
重试请求拦截器 | 1 |
记录器拦截器 | 5 |
该拦截器默认关闭,当幂等请求出现网络异常时,拦截器会随机重试。就像发送“GET”请求时的连接超时一样。
如果你想使用它,你应该在RetrofitConfig
中设置maxTry
和timeout
。
您可以在RetrofitConfig
中设置:
{
"maxTry": "number",
"retryCondition": "RetryCondition"
}
RetryCondition
是一个接口,如果RetryCondition.handler
返回true,拦截器将尝试再次发送请求。
该拦截器默认禁用,它将记录所有请求/响应(或错误)并将其打印在控制台上。
您可以在RetrofitConfig
中设置:
{
"debug": "boolean"
}
您可以轻松取消请求。
let tick = testingClient . demo1 ( ... args ) ;
// Careful the different between micro task and macro task
setTimeout ( ( ) => tick . cancel ( "your message" ) , 3000 ) ;
注意, cancel
api是RetrofitPromise
对象的一个方法,你不能用其他promise对象调用,这是一个例子:
// It is wrong.
tick . then ( ( ) => { ... you code } ) . cancel ( "message" ) ;
当您取消请求时,将会抛出RequestCancelException
,您可以在错误处理程序中处理或忽略。
如果你想统一处理所有异常,只需实现ErrorHandler
即可。
class MyErrorHandler implement ErrorHandler {
public handler ( realReason : any , exception : Exception ) : void {
// your code
}
}
realReason
是 axios 给出的参数, exception
是Exception
的实例,可以方便地统一处理所有异常。
这都是例外:
例外 | 描述 |
---|---|
请求取消异常 | 用户取消 |
连接异常 | ECONNREFUSED 信号激活 |
套接字异常 | ECONNRESET 信号激活 |
请求超时异常 | ECONNABORTED 或 ETIMEDOUT 信号已激活 |
IO异常 | 其余情况未知 |
虽然错误处理程序可以捕获所有异常,但这并不意味着Promise.catch
不会被激活。事实上,有必要在抛出异常时终止正常进程。
class MyErrorHandler implement ErrorHandler {
public handler ( realReason : any , exception : Exception ) : void {
// Your code
}
}
testingClient . demo1 ( ... args ) . then ( response => {
// Normal business process
} ) . catch ( reason => {
// Active after error handler call
} ) ;
在单个方法标志中,不能用多个装饰器来装饰单个参数。
// It is wrong!
public demo1 < T > ( @ Field ( "key1" ) @ Header ( "header1" ) val1 : any ) : RetrofitPromise < T > & void {
}
如果你想创建像https://127.0.0.1/demo?key1=val1&key2=val2
这样的 url 查询,只需按如下操作:
public demo1 < T > ( @ Query ( "key1" ) val1 : any , @ QueryMap map1 : object ) : RetrofitPromise < T > & void {
}
@Query
声明一个查询键值条目。@QueryMap
声明一个查询多键值条目。轻松提交表格。
@ FormUrlEncoded
public demo1 < T > ( @ Field ( "key1" ) val1 : any , @ FieldMap map1 : object ) : RetrofitPromise < T > & void {
}
@Field
和@FieldMap
仅在@FormUrlEncoded
声明方法时有效。
@FormUrlEncoded
声明这是一个表单。@Field
声明一个表单键值条目。@FieldMap
声明一个表单多键值条目。如果要使用json body请求,可以使用@Body
来修饰参数。
public demo1 < T > ( @ Body myBody : object ) : RetrofitPromise < T > & void {
}
@Body
不能与@FormUrlEncoded
或@MultiPart
一起使用,因为单个请求中有一个主体。另外, @Body
不能在同一方法中使用多个符号。
// It is wrong!
public demo1 < T > ( @ Body myBody : object , @ Body otherBody : object ) : RetrofitPromise < T > & void {
}
与上面的情况一样,参数myBody
将被忽略。
如果要覆盖配置,请使用@Config
来装饰参数。
public demo1 < T > ( @ Config config : RetrofitRequest ) : RetrofitPromise < T > & void {
}
它将被参数配置包含的字段覆盖装饰器设置(但不包括全局配置)。
动态配置只在请求时有效,对拦截器配置无效,因为拦截器是在调用Retrofit.getBuilder().build()
时初始化的,并且只初始化一次。
您可以使用RetrofitJs轻松上传文件,如下所示:
@ MultiPart
@ PUT ( "/upload" )
public upload ( @ Part ( "file" ) file : any , @ PartMap anything : any ) : RetrofitPromise < void > & void {
}
这是浏览器方式:
// document.getElementById( "file" ) is a input tag
client . upload ( document . getElementById ( "file" ) . files [ 0 ] ) ;
这是节点方式:
// create a file read stream as parameter, done.
client . upload ( fs . createReadStream ( "your file path" ) ) ;
与表单提交一样, @Part
和@PartMap
也仅在@MultiPart
声明方法时有效。
@MultiPart
声明这是一个表单。@Part
声明表单键值条目。@PartMap
声明一个表单多键值条目。此外,您还必须小心服务器中的最大文件大小设置。如果文件大小大于服务器限制,它总是上传失败。
最后,不要使用Buffer
对象作为参数,我尝试使用Buffer对象上传,但全部失败,因为Buffer对象只有数据,没有任何描述,如文件名、文件类型。
在浏览器中,无法通过Ajax下载文件,因为Ajax总是以字符串数据响应,但您可以使用标签iframe来主动浏览器下载。
您可以在节点上下载文件,如下所示:
@ ResponseBody ( ResponseType . STREAM )
public demo1 ( ) : RetrofitPromise < Stream > & void {
}
@ResponseBody
将告诉 RetrofitJs 应返回什么类型。
这是所有支持的响应类型:
类型 | 价值 |
---|---|
目的 | ResponseType.JSON(默认) |
细绳 | 响应类型.DOCUMENT、响应类型.TEXT |
溪流 | 响应类型.STREAM |
缓冲 | 响应类型.ARRAY_BUFFER |
这是最后一章了,可以看到,RetrofitJs只提供了一个平台,所有的http接口都必须自己写。
您也可以编写一个通用接口并对其进行扩展,然后信息收集器的工作方式如下:
简而言之,信息优先级链如下:@Config > 方法 > 本类 > 超类
麻省理工学院