Client HTTP déclaratif basé sur Axios pour le navigateur et node.js.
Écrit par un programmeur Java habitué au client HTTP déclaratif.
L'objectif est de fournir un client http simple et concis dans tous les environnements javascript/typescript (environnement es6, en fait) ( pas encore! )
Parce que babel-plugin-transform-decorators-legacy
ne prend pas encore en charge le décorateur de paramètres, de sorte que RetrofitJs ne fonctionne que sur l'environnement TypeScript pour le moment.
Citez la proposition ici :
Vous pouvez décorer toute la classe, ainsi que les déclarations de champs, getters, setters et méthodes. Les arguments et les déclarations de fonctions ne peuvent pas être décorés. -- de propositions-décorateurs
et aussi quelqu'un a fait cette demande --> Parameter Decorators ?
Maintenant, concentrons-nous sur la déclaration de notre propre interface http plutôt que sur les détails http, tout comme Retrofit l'a fait, écrivons moins, faisons plus.
Enfin, merci beaucoup à toutes les personnes qui ont écrit ou mis à jour Axios (client http javascript) et Retrofit (client java http), ce sont des projets merveilleux, cela m'apprend beaucoup.
Ce programme ne peut pas fonctionner sur IE8/9/10/11 et d'autres environnements (généralement un navigateur) qui ne prennent pas en charge es6 en natif, car il dépend de l'objet Proxy
(es6) et de la fonctionnalité Decorator
(étape 2).
En d’autres termes, RetrofitJs peut fonctionner sur n’importe quel environnement prenant en charge es6 en natif.
À propos de l'objet Proxy
, je ne trouve aucun polyFill pris en charge. Mais pour le Decorator
, vous pouvez l'utiliser avec quelque chose de supporté par Babel comme babel-plugin-transform-decorators-legacy
.
Utilisation de 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 ( ) ;
Comme Retrofit, RetrofitJs est également implémenté par une chaîne d'intercepteurs. Tous les intercepteurs sont triés par champ order
et triés du plus grand au plus petit .
// This is a interceptor interface
export interface Interceptor {
order : number ;
init ( config : RetrofitConfig ) : void ;
intercept ( chain : Chain ) : Promise < ResponseInterface < any > >
}
Attention, l'intercepteur RealCall
est le dernier intercepteur, et il doit l'être.
C'est un intercepteur par défaut, la valeur du champ order
est nulle. RetrofitJs l'a utilisé pour envoyer toutes les requêtes http par objet Axios.
Pour éviter les conflits, les numéros de champ de commande inférieurs à 256 (sans compter 256) sont réservés.
Vous pouvez facilement implémenter votre propre intercepteur, juste comme ça.
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 ( ) ) ;
}
}
Notez chain.proceed( chain.request() )
, ce code décide si la demande va continuer.
Si vous appelez avec chain.proceed
, la demande en cours sera transférée au prochain intercepteur. Sinon, l'intercepteur de repos ne sera pas actif. Et l'ensemble du processus sera terminé et le gestionnaire d'erreurs sera actif si une erreur est générée par la chaîne d'intercepteur.
En fait, les plugins sont des intercepteurs, vous pouvez écrire votre propre plugin pour RetrofitJs.
Retrofit.use( new MyInterceptor implements Interceptor {
public order: number = 20;
public init( config: RetrofitConfig ): void {
}
public intercept( chain: Chain ): Promise<ResponseInterface<any>> {
}
} );
Il ajoutera tous les intercepteurs à Retrofit
lorsque vous créerez une instance.
Il n'y a pas de limite au champ de commande des plugins. En fait, ces numéros sont réservés aux plugins et aux intercepteurs par défaut.
Et tout cela est un intercepteur par défaut :
intercepteur | commande |
---|---|
Appel réel | 0 |
RetryRequestInterceptor | 1 |
EnregistreurIntercepteur | 5 |
Cet intercepteur est désactivé par défaut, il réessayera à un moment aléatoire lorsque la demande idempotente aura une anomalie de réseau. comme le délai d'expiration de la connexion lorsque vous envoyez une requête 'GET'.
Si vous souhaitez l'utiliser, vous devez définir maxTry
et timeout
dans RetrofitConfig
.
Vous pouvez définir dans RetrofitConfig
:
{
"maxTry": "number",
"retryCondition": "RetryCondition"
}
RetryCondition
est une interface, l'intercepteur tentera d'envoyer à nouveau la demande si RetryCondition.handler
renvoie true.
Cet intercepteur est désactivé par défaut, il enregistrera toutes les requêtes/réponses (ou erreurs) et les imprimera sur la console.
Vous pouvez définir dans RetrofitConfig
:
{
"debug": "boolean"
}
Vous pouvez facilement annuler une demande.
let tick = testingClient . demo1 ( ... args ) ;
// Careful the different between micro task and macro task
setTimeout ( ( ) => tick . cancel ( "your message" ) , 3000 ) ;
Attention, l'API cancel
est une méthode de l'objet RetrofitPromise
, vous ne pouvez pas appeler avec un autre objet de promesse, voici un exemple :
// It is wrong.
tick . then ( ( ) => { ... you code } ) . cancel ( "message" ) ;
Lorsque vous annulez la demande, une RequestCancelException
sera lancée, vous pouvez la gérer ou l'ignorer dans le gestionnaire d'erreurs.
Si vous souhaitez gérer de manière uniforme toutes les exceptions, implémentez simplement ErrorHandler
.
class MyErrorHandler implement ErrorHandler {
public handler ( realReason : any , exception : Exception ) : void {
// your code
}
}
realReason
est le paramètre donné par axios, et exception
est l'instance de Exception
, vous pouvez facilement traiter uniformément toutes les exceptions.
Tout ceci est une exception :
exception | description |
---|---|
DemandeAnnulerException | Annulation de l'utilisateur |
ConnectException | Signal ECONNREFUSED activé |
SocketException | Signal ECONNRESET activé |
RequestTimeoutException | Signal ECONABORTED ou ETIMEDOUT activé |
IOException | le reste de la situation inconnue |
Bien que le gestionnaire d'erreurs puisse intercepter toutes les exceptions, cela ne signifie pas que Promise.catch
ne sera pas actif. En fait, il est nécessaire pour cette raison de mettre fin au processus normal lorsqu'une exception a été levée.
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
} ) ;
Dans un panneau à méthode unique, on ne peut pas décorer un seul paramètre avec plusieurs décorateurs.
// It is wrong!
public demo1 < T > ( @ Field ( "key1" ) @ Header ( "header1" ) val1 : any ) : RetrofitPromise < T > & void {
}
Si vous souhaitez créer une requête URL comme https://127.0.0.1/demo?key1=val1&key2=val2
, procédez simplement comme suit :
public demo1 < T > ( @ Query ( "key1" ) val1 : any , @ QueryMap map1 : object ) : RetrofitPromise < T > & void {
}
@Query
déclare une entrée clé-valeur de requête.@QueryMap
déclare une requête à entrées multi-clés-valeurs.Soumettre facilement le formulaire.
@ FormUrlEncoded
public demo1 < T > ( @ Field ( "key1" ) val1 : any , @ FieldMap map1 : object ) : RetrofitPromise < T > & void {
}
Les @Field
et @FieldMap
ne sont efficaces que lorsque la méthode a été déclarée par @FormUrlEncoded
.
@FormUrlEncoded
déclare qu'il s'agit d'un formulaire.@Field
déclare une entrée clé-valeur de formulaire.@FieldMap
déclare un formulaire à entrées multi-clés-valeurs. Si vous souhaitez effectuer une requête avec un corps json, utilisez @Body
pour décorer le paramètre.
public demo1 < T > ( @ Body myBody : object ) : RetrofitPromise < T > & void {
}
@Body
ne peut pas être utilisé avec @FormUrlEncoded
ou @MultiPart
, car il y a un seul corps dans une seule requête. De plus, @Body
ne peut pas en utiliser plus d'un dans le même signe de méthode.
// It is wrong!
public demo1 < T > ( @ Body myBody : object , @ Body otherBody : object ) : RetrofitPromise < T > & void {
}
Comme dans le cas ci-dessus, le paramètre myBody
sera ignoré.
Si vous souhaitez remplacer la configuration, utilisez @Config
pour décorer le paramètre.
public demo1 < T > ( @ Config config : RetrofitRequest ) : RetrofitPromise < T > & void {
}
Il s'agira de remplacer le paramètre du décorateur (mais sans inclure la configuration globale) par le champ que contient le paramètre config.
La configuration dynamique n'est efficace que dans les requêtes, mais pas dans la configuration de l'intercepteur, car l'intercepteur s'initialise lorsque vous appelez Retrofit.getBuilder().build()
et ne s'initialise qu'une seule fois.
Vous pouvez facilement télécharger un fichier avec RetrofitJs, comme suit :
@ MultiPart
@ PUT ( "/upload" )
public upload ( @ Part ( "file" ) file : any , @ PartMap anything : any ) : RetrofitPromise < void > & void {
}
Voici la méthode du navigateur :
// document.getElementById( "file" ) is a input tag
client . upload ( document . getElementById ( "file" ) . files [ 0 ] ) ;
Et voici la méthode des nœuds :
// create a file read stream as parameter, done.
client . upload ( fs . createReadStream ( "your file path" ) ) ;
Comme la soumission du formulaire, @Part
et @PartMap
ne sont également efficaces que lorsque la méthode a été déclarée par @MultiPart
.
@MultiPart
déclare qu'il s'agit d'un formulaire.@Part
déclare une entrée clé-valeur de formulaire.@PartMap
déclare un formulaire à entrées multi-clés et valeurs.Vous devez également faire attention au paramètre de taille maximale de fichier sur votre serveur. Le téléchargement échoue toujours si la taille du fichier est supérieure à la limite de votre serveur.
Enfin, n'utilisez pas l'objet Buffer
comme paramètre, j'essaie d'utiliser l'objet tampon pour télécharger, mais tout a échoué car l'objet tampon n'a que des données mais aucune description telle que le nom de fichier, le type de fichier.
Dans le navigateur, il n'y a aucun moyen de télécharger le fichier par Ajax car Ajax répond toujours avec des données de chaîne, mais vous pouvez utiliser la balise iframe pour activer le téléchargement du navigateur.
Vous pouvez télécharger le fichier sur le nœud, comme suit :
@ ResponseBody ( ResponseType . STREAM )
public demo1 ( ) : RetrofitPromise < Stream > & void {
}
@ResponseBody
indiquera aux RetrofitJs quel type doit être renvoyé.
Tout cela correspond au type de réponse pris en charge :
taper | valeur |
---|---|
objet | ResponseType.JSON (par défaut) |
chaîne | ResponseType.DOCUMENT, ResponseType.TEXT |
Flux | ResponseType.STREAM |
Tampon | ResponseType.ARRAY_BUFFER |
Ceci est le dernier chapitre, comme vous pouvez le voir, RetrofitJs fournit uniquement une plate-forme et toute l'interface http doit être écrite par vous-même.
Vous pouvez également écrire une interface commune et l'étendre , puis un collecteur d'informations fonctionnant comme suit :
En bref, la chaîne de priorité des informations suit ceci : @Config > méthode > cette classe > super classe
MIT