Cliente HTTP declarativo basado en Axios para el navegador y node.js.
Escrito por un programador de Java que se acostumbró al cliente HTTP declarativo.
El objetivo es proporcionar un cliente http simple y conciso en todos los entornos javascript/typescript (en realidad, entorno es6). ( ¡aún no! )
Debido a que babel-plugin-transform-decorators-legacy
aún no admite el decorador de parámetros, RetrofitJs solo funciona en el entorno TypeScript por ahora.
Cite la propuesta aquí:
Puedes decorar toda la clase, así como declaraciones de campos, getters, setters y métodos. Los argumentos y las declaraciones de funciones no se pueden decorar. -- de decoradores de propuestas
y también alguien presentó esta solicitud -> ¿Decoradores de parámetros?
Ahora, centrémonos en declarar nuestra propia interfaz http en lugar de los detalles de http, tal como lo hizo Retrofit, escriba menos, haga más.
Por último, gracias a todas las personas que escribieron o actualizaron Axios (cliente http de javascript) y Retrofit (cliente http de java), esos son proyectos maravillosos, me enseñaron mucho.
Este programa no puede funcionar en IE8/9/10/11 y otros entornos (generalmente navegador) que no admiten es6 en forma nativa, porque depende del objeto Proxy
(es6) y la función Decorator
(etapa 2).
En otras palabras, RetrofitJs puede funcionar en cualquier entorno que admita es6 de forma nativa.
Acerca del objeto Proxy
, no puedo encontrar ningún polyFill compatible. Pero para Decorator
, puedes usarlo con algo que sea compatible con 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 ( ) ;
Al igual que Retrofit, RetrofitJ también se implementa mediante cadena interceptora. Todos los interceptores se clasifican por campo order
y se clasifican de mayor a menor .
// This is a interceptor interface
export interface Interceptor {
order : number ;
init ( config : RetrofitConfig ) : void ;
intercept ( chain : Chain ) : Promise < ResponseInterface < any > >
}
Tenga cuidado, el interceptor RealCall
es el último interceptor, y debe serlo.
Es un interceptor predeterminado, el valor del campo order
es cero. RetrofitJs lo usó para enviar todas las solicitudes http por objeto Axios.
Para evitar conflictos, los números del campo de pedido inferiores a 256 (sin incluir 256) están reservados.
Puedes implementar fácilmente tu propio interceptor, así.
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 ( ) ) ;
}
}
Tenga en cuenta que chain.proceed( chain.request() )
, este código decide si la solicitud continuará.
Si llama con chain.proceed
, la solicitud actual se transferirá al siguiente interceptor. De lo contrario, el interceptor de descanso no estará activo. Y todo el proceso finalizará y activará el controlador de errores si se produce algún error desde la cadena del interceptor.
De hecho, los complementos son interceptores. Puede escribir su propio complemento para RetrofitJs.
Retrofit.use( new MyInterceptor implements Interceptor {
public order: number = 20;
public init( config: RetrofitConfig ): void {
}
public intercept( chain: Chain ): Promise<ResponseInterface<any>> {
}
} );
Agregará todos los interceptores a Retrofit
cuando cree una instancia.
No hay límite para el campo de pedido de complementos. De hecho, hay números reservados para complementos e interceptores predeterminados.
Y todo este es el interceptor predeterminado:
interceptador | orden |
---|---|
llamada real | 0 |
ReintentarSolicitudInterceptor | 1 |
registradorinterceptor | 5 |
Este interceptor está deshabilitado de forma predeterminada, lo volverá a intentar con un tiempo aleatorio cuando la solicitud idempotente tenga una anomalía en la red. como el tiempo de espera de conexión cuando envía la solicitud 'GET'.
Si desea usarlo, debe configurar maxTry
y timeout
en RetrofitConfig
.
Puede configurar en RetrofitConfig
:
{
"maxTry": "number",
"retryCondition": "RetryCondition"
}
RetryCondition
es una interfaz, el interceptor intentará enviar la solicitud nuevamente si RetryCondition.handler
devuelve verdadero.
Este interceptor está deshabilitado de forma predeterminada, registrará todas las solicitudes/respuestas (o errores) y las imprimirá en la consola.
Puede configurar en RetrofitConfig
:
{
"debug": "boolean"
}
Puede cancelar fácilmente una solicitud.
let tick = testingClient . demo1 ( ... args ) ;
// Careful the different between micro task and macro task
setTimeout ( ( ) => tick . cancel ( "your message" ) , 3000 ) ;
Cuidado, la API cancel
es un método del objeto RetrofitPromise
, no puedes llamar con otro objeto de promesa, este es un ejemplo:
// It is wrong.
tick . then ( ( ) => { ... you code } ) . cancel ( "message" ) ;
Cuando cancele la solicitud, se generará una RequestCancelException
, que puede manejar o ignorar en el controlador de errores.
Si desea un manejo uniforme de todas las excepciones, simplemente implemente ErrorHandler
.
class MyErrorHandler implement ErrorHandler {
public handler ( realReason : any , exception : Exception ) : void {
// your code
}
}
realReason
es el parámetro proporcionado por axios, y exception
es la instancia de Exception
, puede procesar fácilmente de manera uniforme todas las excepciones.
Todo esto es una excepción:
excepción | descripción |
---|---|
SolicitudCancelarExcepción | Cancelación de usuario |
ConexiónExcepción | Señal ECONNREFUSED activada |
Excepción de socket | Señal ECONNRESET activada |
Solicitud de excepción de tiempo de espera | Señal ECONNABORTED o ETIMEDOUT activada |
IOExcepción | el resto de situación desconocida |
Aunque el controlador de errores puede detectar todas las excepciones, eso no significa que Promise.catch
no esté activo. De hecho, es necesario por el motivo de finalizar el proceso normal cuando se produce una excepción.
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
} ) ;
En un signo de método único, no se puede decorar un solo parámetro con un decorador múltiple.
// It is wrong!
public demo1 < T > ( @ Field ( "key1" ) @ Header ( "header1" ) val1 : any ) : RetrofitPromise < T > & void {
}
Si desea crear una consulta de URL como https://127.0.0.1/demo?key1=val1&key2=val2
, simplemente hágalo de la siguiente manera:
public demo1 < T > ( @ Query ( "key1" ) val1 : any , @ QueryMap map1 : object ) : RetrofitPromise < T > & void {
}
@Query
declara una entrada de valor-clave de consulta.@QueryMap
declara una consulta con entradas de valores-clave múltiples.Formulario de envío fácil.
@ FormUrlEncoded
public demo1 < T > ( @ Field ( "key1" ) val1 : any , @ FieldMap map1 : object ) : RetrofitPromise < T > & void {
}
@Field
y @FieldMap
solo son efectivos cuando el método ha sido declarado por @FormUrlEncoded
.
@FormUrlEncoded
declara que este es un formulario.@Field
declara una entrada de valor clave del formulario.@FieldMap
declara un formulario con entradas de valores-clave múltiples. Si desea realizar una solicitud con un cuerpo json, use @Body
para decorar el parámetro.
public demo1 < T > ( @ Body myBody : object ) : RetrofitPromise < T > & void {
}
@Body
no se puede usar con @FormUrlEncoded
o @MultiPart
porque hay un cuerpo en una sola solicitud. Además, @Body
no puede usar más de uno en el mismo signo de método.
// It is wrong!
public demo1 < T > ( @ Body myBody : object , @ Body otherBody : object ) : RetrofitPromise < T > & void {
}
Como en el caso anterior, el parámetro myBody
se ignorará.
Si desea anular la configuración, use @Config
para decorar el parámetro.
public demo1 < T > ( @ Config config : RetrofitRequest ) : RetrofitPromise < T > & void {
}
Se anulará la configuración del decorador (pero sin incluir la configuración global) por el campo que contiene el parámetro de configuración.
La configuración dinámica solo es efectiva en la solicitud, pero no en la configuración del interceptor, porque el interceptor se inicializa cuando llama a Retrofit.getBuilder().build()
y solo se inicializa una vez.
Puede cargar archivos fácilmente con RetrofitJs, como se muestra a continuación:
@ MultiPart
@ PUT ( "/upload" )
public upload ( @ Part ( "file" ) file : any , @ PartMap anything : any ) : RetrofitPromise < void > & void {
}
Esta es la forma del navegador:
// document.getElementById( "file" ) is a input tag
client . upload ( document . getElementById ( "file" ) . files [ 0 ] ) ;
Y esta es la forma del nodo:
// create a file read stream as parameter, done.
client . upload ( fs . createReadStream ( "your file path" ) ) ;
Al igual que el envío del formulario, @Part
y @PartMap
también solo son efectivos cuando el método ha sido declarado por @MultiPart
.
@MultiPart
declara que este es un formulario.@Part
declara una entrada de valor-clave de formulario.@PartMap
declara un formulario con entradas de valores-clave múltiples.También debe tener cuidado con la configuración del tamaño máximo de archivo en su servidor. La carga siempre falla si el tamaño del archivo supera el límite de su servidor.
Por último, no use el objeto Buffer
como parámetro. Intento usar el objeto de búfer para cargar, pero todo falló porque el objeto de búfer solo tiene datos pero no ninguna descripción, como nombre de archivo o tipo de archivo.
En el navegador, no hay forma de descargar archivos mediante Ajax porque Ajax siempre responde con datos de cadena, pero puede usar la etiqueta iframe para activar la descarga del navegador.
Puede descargar el archivo en el nodo, de la siguiente manera:
@ ResponseBody ( ResponseType . STREAM )
public demo1 ( ) : RetrofitPromise < Stream > & void {
}
@ResponseBody
le dirá a los RetrofitJ qué tipo se debe devolver.
Todo este es el tipo de respuesta admitido:
tipo | valor |
---|---|
objeto | Tipo de respuesta.JSON (predeterminado) |
cadena | Tipo de respuesta.DOCUMENTO, Tipo de respuesta.TEXTO |
Arroyo | Tipo de respuesta.STREAM |
Buffer | Tipo de respuesta.ARRAY_BUFFER |
Este es el último capítulo, como puede ver, RetrofitJs proporciona solo una plataforma y toda la interfaz http debe escribirla usted mismo.
También puede escribir una interfaz común y ampliarla , luego el recopilador de información funciona de la siguiente manera:
En resumen, la cadena de prioridad de información sigue esto: @Config > método > esta clase > superclase
MIT