Axios-basierter deklarativer HTTP-Client für den Browser und node.js.
Geschrieben von einem Java-Programmierer, der sich an deklarative HTTP-Clients gewöhnt hat.
Das Ziel besteht darin, einen einfachen, übersichtlichen http-Client in allen Javascript-/Typescript-Umgebungen (eigentlich ES6-Umgebung) bereitzustellen. ( Noch nicht! )
Da babel-plugin-transform-decorators-legacy
den Parameter-Decorator noch nicht unterstützt, funktionieren RetrofitJs vorerst nur in der TypeScript-Umgebung.
Zitieren Sie den Vorschlag hier:
Sie können die gesamte Klasse sowie Deklarationen von Feldern, Gettern, Settern und Methoden dekorieren. Argumente und Funktionsdeklarationen können nicht dekoriert werden. – von Proposal-Decorators
und auch jemand hat diese Anfrage gestellt -> Parameter-Dekoratoren?
Konzentrieren wir uns nun auf die Deklaration einer eigenen http-Schnittstelle und nicht auf http-Details, genau wie Retrofit: Weniger schreiben, mehr tun.
Abschließend möchte ich allen danken, die Axios (Javascript-HTTP-Client) und Retrofit (Java-HTTP-Client) geschrieben oder aktualisiert haben. Das sind wunderbare Projekte, die mir viel beigebracht haben.
Dieses Programm kann nicht auf IE8/9/10/11 und anderen Umgebungen (normalerweise Browser) funktionieren, die es6 nicht nativ unterstützen, da es vom Proxy
Objekt (es6) und Decorator
-Funktion (Stufe 2) abhängt.
Mit anderen Worten: RetrofitJs können in jeder Umgebung arbeiten, die es6 nativ unterstützt.
Über Proxy
Objekt kann ich kein unterstütztes PolyFill finden. Aber für den Decorator
können Sie ihn mit etwas Babel-Unterstützung wie babel-plugin-transform-decorators-legacy
verwenden.
Mit 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 ( ) ;
Wie Retrofit werden auch RetrofitJs durch eine Interceptor-Kette implementiert. Alle Interceptoren werden nach order
sortiert und vom größten zum kleinsten sortiert .
// This is a interceptor interface
export interface Interceptor {
order : number ;
init ( config : RetrofitConfig ) : void ;
intercept ( chain : Chain ) : Promise < ResponseInterface < any > >
}
Seien Sie vorsichtig, der RealCall
-Interceptor ist der letzte Interceptor, und das muss auch so sein.
Es handelt sich um einen Standard-Interceptor, der Wert des order
ist Null. RetrofitJs hat es verwendet, um alle HTTP-Anfragen per Axios-Objekt zu senden.
Um Konflikte zu vermeiden, sind die Nummern des Bestellfelds kleiner als 256 (ohne 256) reserviert.
So können Sie ganz einfach Ihren eigenen Abfangjäger implementieren.
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 ( ) ) ;
}
}
Beachten Sie, chain.proceed( chain.request() )
dieser Code entscheidet, ob die Anfrage fortgesetzt wird.
Wenn Sie mit chain.proceed
aufrufen, wird die aktuelle Anfrage an den nächsten Interceptor weitergeleitet. Andernfalls ist der Rest-Abfangjäger nicht aktiv. Und der gesamte Prozess wird beendet und ist ein aktiver Fehlerbehandler, wenn ein Fehler aus der Interceptor-Kette ausgelöst wird.
Tatsächlich sind Plugins Abfangjäger. Sie können Ihr eigenes Plugin für RetrofitJs schreiben.
Retrofit.use( new MyInterceptor implements Interceptor {
public order: number = 20;
public init( config: RetrofitConfig ): void {
}
public intercept( chain: Chain ): Promise<ResponseInterface<any>> {
}
} );
Beim Erstellen einer Instanz werden alle Abfangjäger zu Retrofit
hinzugefügt.
Dem Bestellfeld der Plugins sind keine Grenzen gesetzt. Tatsächlich sind diese Nummern für Plugins und Standard-Interceptoren reserviert.
Und das ist alles der Standard-Interceptor:
Abfangjäger | Befehl |
---|---|
RealCall | 0 |
RetryRequestInterceptor | 1 |
LoggerInterceptor | 5 |
Dieser Interceptor ist standardmäßig deaktiviert. Er versucht es mit zufälliger Zeit erneut, wenn bei idempotenten Anfragen eine Netzwerkanomalie vorliegt. wie zum Beispiel eine Verbindungszeitüberschreitung, wenn Sie eine „GET“-Anfrage senden.
Wenn Sie es verwenden möchten, sollten Sie maxTry
und timeout
in RetrofitConfig
festlegen.
Sie können in RetrofitConfig
einstellen:
{
"maxTry": "number",
"retryCondition": "RetryCondition"
}
RetryCondition
ist eine Schnittstelle. Der Interceptor versucht erneut, die Anfrage zu senden, wenn RetryCondition.handler
„true“ zurückgibt.
Dieser Interceptor ist standardmäßig deaktiviert, er zeichnet alle Anfragen/Antworten (oder Fehler) auf und gibt sie auf der Konsole aus.
Sie können in RetrofitConfig
einstellen:
{
"debug": "boolean"
}
Sie können eine Anfrage ganz einfach stornieren.
let tick = testingClient . demo1 ( ... args ) ;
// Careful the different between micro task and macro task
setTimeout ( ( ) => tick . cancel ( "your message" ) , 3000 ) ;
Vorsicht, die cancel
-API ist eine Methode des RetrofitPromise
-Objekts. Sie können nicht mit anderen Promise-Objekten aufgerufen werden. Dies ist ein Beispiel:
// It is wrong.
tick . then ( ( ) => { ... you code } ) . cancel ( "message" ) ;
Wenn Sie die Anfrage abbrechen, wird eine RequestCancelException
ausgelöst, die Sie im Fehlerhandler behandeln oder ignorieren können.
Wenn Sie alle Ausnahmen einheitlich behandeln möchten, implementieren Sie einfach ErrorHandler
.
class MyErrorHandler implement ErrorHandler {
public handler ( realReason : any , exception : Exception ) : void {
// your code
}
}
realReason
ist der von axios angegebene Parameter, exception
ist die Instanz von Exception
. Sie können alle Ausnahmen problemlos einheitlich verarbeiten.
Dies ist alles eine Ausnahme:
Ausnahme | Beschreibung |
---|---|
RequestCancelException | Kündigung durch den Benutzer |
ConnectException | ECONNREFUSED-Signal aktiviert |
SocketException | ECONNRESET-Signal aktiviert |
RequestTimeoutException | ECONNABORTED- oder ETIMEDOUT-Signal aktiviert |
IOException | der Rest der unbekannten Situation |
Obwohl der Fehlerhandler alle Ausnahmen abfangen kann, bedeutet dies nicht, Promise.catch
nicht aktiv ist. Tatsächlich ist es aus dem Grund notwendig, den normalen Prozess zu beenden, wenn eine Ausnahme ausgelöst wurde.
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
} ) ;
Bei einer einzelnen Zeichenmethode kann kein einzelner Parameter mit einem Multi-Decorator dekoriert werden.
// It is wrong!
public demo1 < T > ( @ Field ( "key1" ) @ Header ( "header1" ) val1 : any ) : RetrofitPromise < T > & void {
}
Wenn Sie eine URL-Abfrage wie https://127.0.0.1/demo?key1=val1&key2=val2
erstellen möchten, gehen Sie einfach wie folgt vor:
public demo1 < T > ( @ Query ( "key1" ) val1 : any , @ QueryMap map1 : object ) : RetrofitPromise < T > & void {
}
@Query
deklariert einen Schlüsselwerteintrag für die Abfrage.@QueryMap
deklariert eine Abfrage mit mehreren Schlüssel-Wert-Einträgen.Einfach einzureichendes Formular.
@ FormUrlEncoded
public demo1 < T > ( @ Field ( "key1" ) val1 : any , @ FieldMap map1 : object ) : RetrofitPromise < T > & void {
}
@Field
und @FieldMap
sind nur wirksam, wenn die Methode durch @FormUrlEncoded
deklariert wurde.
@FormUrlEncoded
erklärt, dass es sich um ein Formular handelt.@Field
deklariert einen Formular-Schlüsselwerteintrag.@FieldMap
deklariert ein Formular mit mehreren Schlüssel-Wert-Einträgen. Wenn Sie eine Anfrage mit einem JSON-Body stellen möchten, verwenden Sie @Body
um den Parameter zu dekorieren.
public demo1 < T > ( @ Body myBody : object ) : RetrofitPromise < T > & void {
}
@Body
kann nicht mit @FormUrlEncoded
oder @MultiPart
verwendet werden, da eine einzelne Anfrage einen Text enthält. Außerdem kann @Body
nicht mehr als ein Zeichen im selben Methodenzeichen verwenden.
// It is wrong!
public demo1 < T > ( @ Body myBody : object , @ Body otherBody : object ) : RetrofitPromise < T > & void {
}
Wie im obigen Fall wird der Parameter myBody
ignoriert.
Wenn Sie die Konfiguration überschreiben möchten, verwenden Sie @Config
um den Parameter zu dekorieren.
public demo1 < T > ( @ Config config : RetrofitRequest ) : RetrofitPromise < T > & void {
}
Die Decorator-Einstellung wird durch das Feld überschrieben (jedoch nicht einschließlich der globalen Konfiguration), dessen Parameter in der Konfiguration enthalten sind.
Die dynamische Konfiguration ist nur bei Anfragen wirksam, nicht jedoch bei der Interceptor-Konfiguration, da der Interceptor beim Aufruf von Retrofit.getBuilder().build()
initialisiert und nur einmal initialisiert wird.
Sie können Dateien ganz einfach mit RetrofitJs hochladen, wie folgt:
@ MultiPart
@ PUT ( "/upload" )
public upload ( @ Part ( "file" ) file : any , @ PartMap anything : any ) : RetrofitPromise < void > & void {
}
Dies ist der Browser-Weg:
// document.getElementById( "file" ) is a input tag
client . upload ( document . getElementById ( "file" ) . files [ 0 ] ) ;
Und das ist der Knotenweg:
// create a file read stream as parameter, done.
client . upload ( fs . createReadStream ( "your file path" ) ) ;
Wie bei der Formularübermittlung sind auch @Part
und @PartMap
nur wirksam, wenn die Methode durch @MultiPart
deklariert wurde.
@MultiPart
erklärt, dass dies ein Formular ist.@Part
deklariert einen Formular-Schlüsselwerteintrag.@PartMap
deklariert ein Formular mit mehreren Schlüssel-Wert-Einträgen.Außerdem müssen Sie auf die Einstellung der maximalen Dateigröße auf Ihrem Server achten. Der Upload schlug immer dann fehl, wenn die Dateigröße größer als Ihr Serverlimit war.
Im letzten Punkt „ Buffer
nicht als Parameter verwenden“ versuche ich, das Pufferobjekt zum Hochladen zu verwenden, aber alles ist fehlgeschlagen, weil das Pufferobjekt nur Daten, aber keine Beschreibung wie Dateiname oder Dateityp enthält.
Im Browser gibt es keine Möglichkeit, Dateien über Ajax herunterzuladen, da Ajax immer mit Zeichenfolgendaten antwortet. Sie können jedoch das Tag iframe für den aktiven Browser-Download verwenden.
Sie können die Datei wie folgt auf den Knoten herunterladen:
@ ResponseBody ( ResponseType . STREAM )
public demo1 ( ) : RetrofitPromise < Stream > & void {
}
@ResponseBody
teilt den RetrofitJs mit, welcher Typ zurückgegeben werden soll.
Dies ist alles ein unterstützter Antworttyp:
Typ | Wert |
---|---|
Objekt | ResponseType.JSON (Standard) |
Zeichenfolge | ResponseType.DOCUMENT, ResponseType.TEXT |
Strom | ResponseType.STREAM |
Puffer | ResponseType.ARRAY_BUFFER |
Dies ist das letzte Kapitel. Wie Sie sehen, stellt RetrofitJs nur eine Plattform bereit und alle http-Schnittstellen müssen selbst geschrieben werden.
Sie können auch eine gemeinsame Schnittstelle schreiben und diese erweitern , dann funktioniert der Informationssammler wie folgt:
Kurz gesagt folgt die Informationsprioritätskette dieser: @Config > Methode > diese Klasse > Superklasse
MIT