Cliente HTTP declarativo baseado em Axios para o navegador e node.js.
Escrito por um programador java que se acostumou com o cliente HTTP declarativo.
O objetivo é fornecer um cliente http simples e conciso em todos os ambientes javascript/typescript, (ambiente es6, na verdade) ( ainda não! )
Porque babel-plugin-transform-decorators-legacy
ainda não suporta decorador de parâmetros, então RetrofitJs só funciona em ambiente TypeScript por enquanto.
Cite a proposta aqui:
Você pode decorar toda a classe, bem como declarações de campos, getters, setters e métodos. Argumentos e declarações de funções não podem ser decorados. - de decoradores de propostas
e também alguém apresentou este pedido -> Decoradores de parâmetros?
Agora, vamos nos concentrar em declarar a própria interface http em vez de detalhes http, assim como fez o Retrofit, escreva menos, faça mais.
Por fim, muito obrigado a todos que escreveram ou atualizaram o Axios (cliente javascript http) e o Retrofit (cliente java http), são projetos maravilhosos, me ensinam muito.
Este programa não pode funcionar no IE8/9/10/11 e outros ambientes (geralmente navegador) que não suportam es6 nativo, porque depende do objeto Proxy
(es6) e do recurso Decorator
(estágio 2).
Em outras palavras, RetrofitJs podem funcionar em qualquer ambiente que suporte es6 nativamente.
Sobre o objeto Proxy
, não consigo encontrar nenhum polyFill compatível. Mas para o Decorator
, você pode usá-lo com algo que suporte babel como babel-plugin-transform-decorators-legacy
.
Usando 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 ( ) ;
Assim como o Retrofit, os RetrofitJs também são implementados por cadeia de interceptadores. Todos os interceptadores são classificados por campo order
e classificados do maior para o menor .
// This is a interceptor interface
export interface Interceptor {
order : number ;
init ( config : RetrofitConfig ) : void ;
intercept ( chain : Chain ) : Promise < ResponseInterface < any > >
}
Tenha cuidado, o interceptador RealCall
é o último interceptador, e deve ser.
É um interceptor padrão, o valor do campo order
é zero. RetrofitJs o usou para enviar todas as solicitações http pelo objeto Axios.
Para evitar conflitos, os números do campo de pedido inferiores a 256 (não incluindo 256) são reservados.
Você pode facilmente implementar seu próprio interceptador, assim.
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 ( ) ) ;
}
}
Observe chain.proceed( chain.request() )
, este código decide se a solicitação continuará.
Se você ligar com chain.proceed
, a solicitação atual será transferida para o próximo interceptor. Caso contrário, o interceptador restante não estará ativo. E todo o processo será encerrado e o manipulador de erros ativo se algum erro for lançado na cadeia do interceptador.
Na verdade, os plugins são interceptadores. Você pode escrever seu próprio plugin para RetrofitJs.
Retrofit.use( new MyInterceptor implements Interceptor {
public order: number = 20;
public init( config: RetrofitConfig ): void {
}
public intercept( chain: Chain ): Promise<ResponseInterface<any>> {
}
} );
Ele adicionará todos os interceptores ao Retrofit
quando você criar uma instância.
Não há limite para o campo de pedido de plug-ins. Na verdade, esses números são reservados para plugins e interceptadores padrão.
E tudo isso é interceptador padrão:
interceptor | ordem |
---|---|
Chamada Real | 0 |
RetryRequestInterceptor | 1 |
LoggerInterceptor | 5 |
Este interceptor está desabilitado por padrão, ele tentará novamente com tempo aleatório quando a solicitação idempotente apresentar uma anomalia na rede. como tempo limite de conexão quando você envia a solicitação 'GET'.
Se quiser usá-lo, você deve definir maxTry
e timeout
em RetrofitConfig
.
Você pode configurar em RetrofitConfig
:
{
"maxTry": "number",
"retryCondition": "RetryCondition"
}
RetryCondition
é uma interface, o interceptador tentará enviar a solicitação novamente se RetryCondition.handler
retornar verdadeiro.
Este interceptor desabilitado por padrão, ele irá registrar todas as solicitações/respostas (ou erros) e imprimi-las no console.
Você pode configurar em RetrofitConfig
:
{
"debug": "boolean"
}
Você pode facilmente cancelar uma solicitação.
let tick = testingClient . demo1 ( ... args ) ;
// Careful the different between micro task and macro task
setTimeout ( ( ) => tick . cancel ( "your message" ) , 3000 ) ;
Cuidado, a API cancel
é um método do objeto RetrofitPromise
, você não pode chamar com outro objeto de promessa, este é um exemplo:
// It is wrong.
tick . then ( ( ) => { ... you code } ) . cancel ( "message" ) ;
Quando você cancela a solicitação, será lançada uma RequestCancelException
, que você pode manipular ou ignorar no manipulador de erros.
Se você deseja tratar de maneira uniforme todas as exceções, basta implementar ErrorHandler
.
class MyErrorHandler implement ErrorHandler {
public handler ( realReason : any , exception : Exception ) : void {
// your code
}
}
realReason
é o parâmetro fornecido por axios, e exception
é a instância de Exception
, você pode facilmente processar de maneira uniforme todas as exceções.
Tudo isso é exceção:
exceção | descrição |
---|---|
SolicitaçãoCancelException | Cancelamento do usuário |
ConnectException | Sinal ECONNREFUSED ativado |
SocketException | Sinal ECONNRESET ativado |
RequestTimeoutException | Sinal ECONNABORTED ou ETIMEDOUT ativado |
IOException | o resto da situação desconhecida |
Embora o manipulador de erros possa ser uma exceção catch all, isso não significa que Promise.catch
não estará ativo. Na verdade, é necessário encerrar o processo normal quando uma exceção é lançada.
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
} ) ;
Em um sinal de método único, não é possível decorar um único parâmetro com multi-decorador.
// It is wrong!
public demo1 < T > ( @ Field ( "key1" ) @ Header ( "header1" ) val1 : any ) : RetrofitPromise < T > & void {
}
Se você deseja criar uma consulta de URL como https://127.0.0.1/demo?key1=val1&key2=val2
, faça o seguinte:
public demo1 < T > ( @ Query ( "key1" ) val1 : any , @ QueryMap map1 : object ) : RetrofitPromise < T > & void {
}
@Query
declara uma entrada de valor-chave de consulta.@QueryMap
declara uma consulta com entradas de vários valores-chave.Facilmente para enviar o formulário.
@ FormUrlEncoded
public demo1 < T > ( @ Field ( "key1" ) val1 : any , @ FieldMap map1 : object ) : RetrofitPromise < T > & void {
}
O @Field
e @FieldMap
só são eficazes quando o método foi declarado por @FormUrlEncoded
.
@FormUrlEncoded
declara que este é um formulário.@Field
declara uma entrada de valor-chave do formulário.@FieldMap
declara um formulário com entradas de vários valores-chave. Se você quiser solicitar com corpo json, use @Body
para decorar o parâmetro.
public demo1 < T > ( @ Body myBody : object ) : RetrofitPromise < T > & void {
}
@Body
não pode ser usado com @FormUrlEncoded
ou @MultiPart
, porque há um corpo em uma única solicitação. Além disso, @Body
não pode usar mais de um sinal no mesmo método.
// It is wrong!
public demo1 < T > ( @ Body myBody : object , @ Body otherBody : object ) : RetrofitPromise < T > & void {
}
Como no caso acima, o parâmetro myBody
será ignorado.
Se você quiser substituir a configuração, use @Config
para decorar o parâmetro.
public demo1 < T > ( @ Config config : RetrofitRequest ) : RetrofitPromise < T > & void {
}
Será substituída a configuração do decorador (mas não incluindo a configuração global) pelo campo que contém a configuração do parâmetro.
A configuração dinâmica só tem efeito na solicitação, mas não na configuração do interceptador, porque o interceptador inicializa quando você chama Retrofit.getBuilder().build()
e inicializa apenas uma vez.
Você pode facilmente fazer upload de arquivos com RetrofitJs, da seguinte forma:
@ MultiPart
@ PUT ( "/upload" )
public upload ( @ Part ( "file" ) file : any , @ PartMap anything : any ) : RetrofitPromise < void > & void {
}
Esta é a maneira do navegador:
// document.getElementById( "file" ) is a input tag
client . upload ( document . getElementById ( "file" ) . files [ 0 ] ) ;
E este é o caminho do nó:
// create a file read stream as parameter, done.
client . upload ( fs . createReadStream ( "your file path" ) ) ;
Assim como o envio do formulário, @Part
e @PartMap
também só são eficazes quando o método foi declarado por @MultiPart
.
@MultiPart
declara que este é um formulário.@Part
declara uma entrada de valor-chave do formulário.@PartMap
declara um formulário com entradas de vários valores-chave.Além disso, você deve ter cuidado com a configuração de tamanho máximo de arquivo em seu servidor. O upload sempre falhou se o tamanho do arquivo for maior que o limite do servidor.
Por último, não use o objeto Buffer
como parâmetro, tento usar o objeto buffer para fazer upload, mas tudo falhou porque o objeto buffer possui apenas dados, mas não qualquer descrição, como nome de arquivo, tipo de arquivo.
No navegador, não há como baixar o arquivo pelo Ajax porque o Ajax sempre responde com dados de string, mas você pode usar a tag iframe para ativar o download do navegador.
Você pode baixar o arquivo no nó, da seguinte forma:
@ ResponseBody ( ResponseType . STREAM )
public demo1 ( ) : RetrofitPromise < Stream > & void {
}
@ResponseBody
informará aos RetrofitJs que tipo deve ser retornado.
Todos estes são tipos de resposta suportados:
tipo | valor |
---|---|
objeto | ResponseType.JSON (padrão) |
corda | ResponseType.DOCUMENT, ResponseType.TEXT |
Fluxo | ResponseType.STREAM |
Tampão | ResponseType.ARRAY_BUFFER |
Este é o último capítulo, como você pode ver, os RetrofitJs fornecem apenas uma plataforma, e toda interface http deve ser escrita por você mesmo.
Além disso, você pode escrever uma interface comum e estendê-la , e então o coletor de informações funcionará da seguinte maneira:
Resumindo, a cadeia de prioridade de informações segue isto: @Config > método > esta classe > superclasse
MIT