DRY bleiben mit Axios für API-Anfragen

Avatar of David Atanda
David Atanda am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

HTTP-Anfragen sind ein wichtiger Bestandteil jeder Webanwendung, die mit einem Back-End-Server kommuniziert. Das Frontend benötigt einige Daten, fragt diese über eine Netzwerk-HTTP-Anfrage (oder Ajax, wie es oft genannt wird) an, und der Server liefert eine Antwort. Fast jede Website macht dies heutzutage in irgendeiner Form.

Bei einer größeren Website können wir mehr davon erwarten. Mehr Daten, mehr APIs und mehr Sonderfälle. Wenn Websites so wachsen, ist es wichtig, organisiert zu bleiben. Ein klassisches Konzept ist DRY (kurz für Don't Repeat Yourself), was der Prozess der Abstraktion von Code ist, um zu verhindern, dass er immer wieder wiederholt wird. Das ist ideal, da es uns oft ermöglicht, etwas einmal zu schreiben, es an mehreren Stellen zu verwenden und es an einer einzigen Stelle zu aktualisieren, anstatt an jeder einzelnen Instanz.

Wir können uns auch an Bibliotheken wenden, um uns zu helfen. Für Ajax ist axios eine beliebte Wahl. Möglicherweise sind Sie bereits damit vertraut und verwenden es sogar für Dinge wie unabhängige POST- und GET-Anfragen während der Entwicklung.

Installation und die Grundlagen

Es kann mit npm (oder yarn) installiert werden

npm install axios

Eine unabhängige POST-Anfrage mit Axios sieht so aus

axios.post('https://axios-app.firebaseio.com/users.json', formData)
  .then(res => console.log(res))
  .catch(error => console.log(error))

Native JavaScript hat auch mehrere Möglichkeiten, JavaScript zu machen. Insbesondere fetch(). Warum also überhaupt eine Bibliothek verwenden? Nun, zum einen ist die Fehlerbehandlung in fetch ziemlich umständlich. Mit axios werden Sie damit von Anfang an eine bessere Erfahrung haben. Wenn Sie einen Vergleich sehen möchten, haben wir einen Artikel, der beide behandelt und einen Artikel, der über den Wert von Abstraktionen bei solchen Dingen spricht.

Ein weiterer Grund, zu axios zu greifen? Es bietet uns mehr Möglichkeiten für DRYness, also schauen wir uns das mal an.

Globale Konfiguration

Wir können eine globale Konfiguration einrichten (z. B. in unserer main.js-Datei), die alle Anwendungsanfragen über **eine Standardkonfiguration** abwickelt, die über ein Standardobjekt gesetzt wird, das mit axios geliefert wird.

Dieses Objekt enthält

  • baseURL: Eine relative URL, die als Präfix für alle Anfragen dient, und jede Anfrage kann die URL anhängen
  • headers: Benutzerdefinierte Header, die basierend auf den Anfragen gesetzt werden können
  • timeout: Der Zeitpunkt, an dem die Anfrage abgebrochen wird, normalerweise in Millisekunden gemessen. Der Standardwert ist 0, was bedeutet, dass er nicht anwendbar ist.
  • withCredentials: Gibt an, ob Cross-Site-Access-Control-Anfragen mit Anmeldeinformationen durchgeführt werden sollen. Der Standardwert ist false.
  • responseType: Gibt den Datentyp an, den der Server zurückgibt, mit Optionen wie json (Standard), arraybuffer, document, text und stream.
  • responseEncoding: Gibt die zu verwendende Kodierung für die Dekodierung von Antworten an. Der Standardwert ist utf8.
  • xsrfCookieName: Der Name des Cookies, der als Wert für das XSRF-Token verwendet wird, der Standardwert ist XSRF-TOKEN.
  • xsrfHeaderName: Der Name des HTTP-Headers, der den XSRF-Token-Wert trägt. Der Standardwert ist X-XSRF-TOKEN.
  • maxContentLength: Definiert die maximal zulässige Größe des HTTP-Antwortinhalts in Bytes
  • maxBodyLength: Definiert die maximal zulässige Größe des HTTP-Anfrageinhalts in Bytes

Meistens werden Sie nur baseURL, header und vielleicht timeout verwenden. Die restlichen sind seltener nötig, da sie intelligente Standardwerte haben, aber es ist gut zu wissen, dass sie vorhanden sind, falls Sie Anfragen korrigieren müssen.

Das ist die DRYness in Aktion. Für jede Anfrage müssen wir nicht die baseURL unserer API wiederholen oder wichtige Header wiederholen, die wir bei jeder Anfrage benötigen könnten.

Hier ist ein Beispiel, bei dem unsere API eine Basis hat, aber auch mehrere verschiedene Endpunkte. Zuerst richten wir einige Standardwerte ein

// main.js
import axios from 'axios';


axios.defaults.baseURL = 'https://axios-app.firebaseio.com' // the prefix of the URL
axios.defaults.headers.get['Accept'] = 'application/json'   // default header for all get request
axios.defaults.headers.post['Accept'] = 'application/json'  // default header for all POST request


Then, in a component, we can use axios more succinctly, not needing to set those headers, but still having an opportunity to customize the final URL endpoint:


// form.js component
import axios from 'axios';


export default {
  methods : {
    onSubmit () {
      // The URL is now https://axios-app.firebaseio.com/users.json
      axios.post('/users.json', formData)
        .then(res => console.log(res))
        .catch(error => console.log(error))
    }
  }
}

Hinweis: Dieses Beispiel ist in Vue, aber das Konzept lässt sich auf jede JavaScript-Situation übertragen.

Benutzerdefinierte Instanz

Das Einrichten einer "benutzerdefinierten Instanz" ähnelt einer globalen Konfiguration, ist aber auf bestimmte Komponenten beschränkt. Es ist also immer noch eine DRY-Technik, aber mit Hierarchie.

Wir richten unsere benutzerdefinierte Instanz in einer neuen Datei ein (nennen wir sie authAxios.js) und importieren sie in die "betroffenen" Komponenten.

// authAxios.js
import axios from 'axios'


const customInstance = axios.create ({
  baseURL : 'https://axios-app.firebaseio.com'
})
customInstance.defaults.headers.post['Accept'] = 'application/json'


// Or like this...
const customInstance = axios.create ({
  baseURL : 'https://axios-app.firebaseio.com',
  headers: {'Accept': 'application/json'}
})

Und dann importieren wir diese Datei in die Formular-Komponenten

// form.js component


// import from our custom instance
import axios from './authAxios'


export default {
  methods : {
    onSubmit () {
      axios.post('/users.json', formData)
      .then(res => console.log(res))
      .catch(error => console.log(error))
    }
  }
}

Interceptors

Interceptors helfen in Fällen, in denen die globale Konfiguration oder die benutzerdefinierte Instanz zu allgemein ist, da sie, wenn Sie einen Header in ihren Objekten einrichten, für den Header jeder Anfrage innerhalb der betroffenen Komponenten gilt. Interceptors können beliebige Objekteigenschaften im laufenden Betrieb ändern. Wir können zum Beispiel einen anderen Header senden (auch wenn wir einen im Objekt eingestellt haben) basierend auf einer beliebigen Bedingung, die wir innerhalb des Interceptors wählen.

Interceptors können sich in der main.js-Datei oder einer benutzerdefinierten Instanzdatei befinden. Anfragen werden abgefangen, nachdem sie gesendet wurden, und ermöglichen es uns, die Behandlung der Antwort zu ändern.

// Add a request interceptor
axios.interceptors.request.use(function (config) {
  // Do something before request is sent, like we're inserting a timeout for only requests with a particular baseURL
  if (config.baseURL === 'https://axios-app.firebaseio.com/users.json') { 
    config.timeout = 4000 
  } else { 
    return config
  }
  console.log (config)
    return config;
  }, function (error) {
  // Do something with request error
  return Promise.reject(error);
});
 
// Add a response interceptor
axios.interceptors.response.use(function (response) {
  // Do something with response data like console.log, change header, or as we did here just added a conditional behaviour, to change the route or pop up an alert box, based on the reponse status  
  if (response.status === 200 || response.status 201) {
    router.replace('homepage') }
  else {
    alert('Unusual behaviour')
  }
  console.log(response)
  return response;
}, function (error) {
  // Do something with response error
  return Promise.reject(error);
});

Interceptors fangen, wie der Name schon sagt, sowohl Anfragen als auch Antworten ab, um basierend auf den bereitgestellten Bedingungen unterschiedlich zu reagieren. Im obigen Anfrage-Interceptor haben wir zum Beispiel ein bedingtes Timeout eingefügt, nur wenn die Anfragen eine bestimmte baseURL haben. Für die Antwort können wir sie abfangen und modifizieren, was wir zurückbekommen, z. B. die Route ändern oder eine Alert-Box anzeigen, abhängig vom Statuscode. Wir können sogar mehrere Bedingungen basierend auf verschiedenen Fehlercodes angeben.

Interceptors werden sich als nützlich erweisen, wenn Ihr Projekt größer wird und Sie viele Routen und verschachtelte Routen haben, die alle basierend auf verschiedenen Auslösern mit Servern kommunizieren. Über die von mir oben genannten Bedingungen hinaus gibt es viele andere Situationen, die die Verwendung von Interceptors rechtfertigen können, basierend auf Ihrem Projekt.

Interessanterweise können wir einen Interceptor auswerfen, um zu verhindern, dass er überhaupt eine Wirkung hat. Wir müssen den Interceptor einer Variablen zuweisen und ihn mit der entsprechend benannten eject-Methode auswerfen.

const reqInterceptor = axios.interceptors.request.use(function (config) {
  // Do something before request is sent, like we're inserting a timeout for only requests with a particular baseURL
  if (config.baseURL === 'https://axios-app.firebaseio.com/users.json') {
    config.timeout = 4000
  } else {
    return config
  }
  console.log (config)
  return config;
}, function (error) {    
  // Do something with request error
  return Promise.reject(error);
});
 
// Add a response interceptor
const resInterceptor = axios.interceptors.response.use(function (response) {
  // Do something with response data like console.log, change header, or as we did here just added a conditional behaviour, to change the route or pop up an alert box, based on the reponse status  
  if (response.status === 200 || response.status 201) {
    router.replace('homepage')
  } else {
    alert('Unusual behaviour')
  }
  console.log(response)
  return response;
}, function (error) {
  // Do something with response error
  return Promise.reject(error);
});


axios.interceptors.request.eject(reqInterceptor);
axios.interceptors.request.eject(resInterceptor);

Obwohl es weniger gebräuchlich ist, ist es möglich, einen Interceptor in eine Bedingung zu setzen oder ihn basierend auf einem Ereignis zu entfernen.


Hoffentlich gibt Ihnen dies eine gute Vorstellung davon, wie axios funktioniert und wie es verwendet werden kann, um API-Anfragen in einer Anwendung DRY zu halten. Während wir die Oberfläche ankratzten, indem wir gängige Anwendungsfälle und Konfigurationen hervorheben, bietet axios viele weitere Vorteile, die Sie in der Dokumentation erkunden können, einschließlich der Möglichkeit, Anfragen abzubrechen und vor Cross-Site Request Forgery zu schützen, unter anderem weitere großartige Möglichkeiten.