Senden einer SMS über React mit Twilio

October 24, 2018
Autor:in:
Phil Nash
Twilion

Senden einer SMS über React mit Twilio


Hallo und Danke fürs Lesen! Dieser Blogpost ist eine Übersetzung von How to send an SMS from React with Twilio. Während wir unsere Übersetzungsprozesse verbessern, würden wir uns über Dein Feedback an help@twilio.com freuen, solltest Du etwas bemerken, was falsch übersetzt wurde. Wir bedanken uns für hilfreiche Beiträge mit Twilio Swag :)

Wir reden viel über das Senden von SMS-Nachrichten über Webanwendungen, aber wie sieht es aus, wenn wir SMS-Nachrichten über eine React-Anwendung senden möchten? Das ist zwar mit etwas mehr Aufwand verbunden, als sich nur um die serverseitige Version zu kümmern, wird aber auch nicht zu viel Zeit in Anspruch nehmen.

Warum eignet sich dazu nicht die Verwendung der REST-API auf Clientseite?

Eigentlich könnten wir eine SMS mit der Twilio-REST-API direkt über eine clientseitige JavaScript-Anwendung senden. Aber (und das ist ein großes Aber) wenn wir das tun, würden wir unsere Twilio-Anmeldeinformationen jedem Benutzer preisgeben, der unsere Seite nutzt. Ein böswilliger Benutzer könnte diese Anmeldeinformationen missbrauchen und zulasten unseres Kontos Schulden anhäufen.

Eine Animation zeigt einen falschen Hacker mit Wollmütze und zusätzlichen Händen.

Live-Aufnahme eines Hackers mit unseren Kontoanmeldeinformationen

Um dieses Risiko zu vermeiden, erstellen wir eine Back-End-Anwendung, die die Twilio-REST-API implementiert, unsere Anmeldeinformationen umschließt und SMS-Nachrichten für uns sendet. Dann können wir unser Back-End über die React-Anwendung aufrufen und SMS-Nachrichten senden, ohne unsere Anmeldeinformationen im Internet preiszugeben.

Unsere Tools

Damit unsere Anwendung Textnachrichten mit der Twilio-REST-API senden kann, benötigen wir Folgendes:

  • Ein Twilio-Konto und eine Twilio-Telefonnummer mit SMS-Nachrichtenfunktion (melde dich hier für ein kostenloses Twilio-Konto an)
  • Node.js zum Erstellen der React-App und zum Ausführen des Servers (die serverseitige Komponente kann zwar in jeder Programmiersprache erstellt werden, aber in diesem Blogbeitrag verwenden wir Node, damit wir alles in JavaScript haben)
  • React-Entwicklertools für den Browser (optional, aber sehr hilfreich, um zu sehen, was in der Anwendung vor sich geht)

Lade zuerst die react-express-starter-Anwendung herunter, die ich in meinem letzten Blogbeitrag erstellt habe, oder klone sie.

git clone https://github.com/philnash/react-express-starter.git

Wechsele in das Verzeichnis und installiere die Abhängigkeiten.

cd react-express-starter
npm install

Erstelle im Projektverzeichnis eine Datei mit dem Namen .env:

touch .env

Mit dem Befehl npm run dev können wir das Projekt testen. Die Anwendung wird im Browser unter „localhost:3000“ geladen.

Diese Startanwendung ist so eingerichtet, dass sowohl eine React-Anwendung als auch eine Express-Anwendung im selben Projekt vorhanden sind, die gleichzeitig ausgeführt werden können. Weitere Informationen zur Funktionsweise findest du in diesem Blogbeitrag.

Erstellen der Serverseite

Wie bereits erwähnt, müssen wir die Twilio-API-Aufrufe vom Server aus durchführen. Wir fügen dem Express-Server einen Endpunkt hinzu, den wir von unserer React-Anwendung aus aufrufen können. Zuerst installieren wir das Twilio-Node.js-Modul. Hinweis: Für die Zwecke dieser Anwendung speichere ich die Serverabhängigkeiten als Entwicklungsabhängigkeiten, um sie von den clientseitigen Abhängigkeiten zu unterscheiden.

npm install twilio --save-dev

Als Nächstes müssen wir die Anwendung mit unseren Twilio-Anmeldeinformationen konfigurieren. Rufe die Twilio-Konto-SID und das Authentifizierungstoken von der Twilio-Konsole ab und besorge dir eine Twilio-Telefonnummer mit SMS-Nachrichtenfunktion. Gib alle drei Informationen folgendermaßen in die zuvor erstellte .env-Datei ein:

TWILIO_ACCOUNT_SID=YOUR_ACCOUNT_SID
TWILIO_AUTH_TOKEN=YOUR_AUTH_TOKEN
TWILIO_PHONE_NUMBER=YOUR_TWILIO_PHONE_NUMBER

Dadurch werden unsere Anmeldeinformationen in der Umgebung festgelegt. Öffne jetzt server/index.js, damit wir mit dem Schreiben des Codes beginnen können, der zum Senden der Nachricht erforderlich ist. Unter den anderen Modulvoraussetzungen am Anfang der Datei fordern wir die Twilio-Bibliothek mit den Anmeldeinformationen aus der Umgebung an und initialisieren sie.

const express = require('express');
const bodyParser = require('body-parser');
const pino = require('express-pino-logger')();
const client = require('twilio')(
  process.env.TWILIO_ACCOUNT_SID,
  process.env.TWILIO_AUTH_TOKEN
);

Wir senden die Daten an den Endpunkt, den wir als JSON erstellen. Wir müssen also in der Lage sein, den JSON-Text zu parsen. Konfiguriere die Express-App mit dem Textparser für JSON:

const app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(pino);

Erstelle eine Route für eine POST-Anfrage. Füge unterhalb der Route für /api/greeting Folgendes hinzu:

app.post('/api/messages', (req, res) => {

});

Unsere Antwort erfolgt ebenfalls in JSON, stelle deshalb den Content-Type-Header auf application/json ein.

app.post('/api/messages', (req, res) => {
  res.header('Content-Type', 'application/json');

});

Anschließend erstellen wir mit dem Twilio-Client, den wir zuvor initialisiert haben, eine Nachricht. Wir verwenden unsere Twilio-Nummer als from-Nummer und rufen die to-Nummer und den body der Nachricht aus dem eingehenden Anfragetext ab. Wir erhalten eine Zusage zurück, die erfüllt wird, wenn die API-Anfrage erfolgreich ist, oder abgelehnt wird, wenn die Anfrage fehlschlägt. Bei beiden Ereignissen geben wir eine JSON-Antwort zurück, um der Clientseite mitzuteilen, ob die Anfrage erfolgreich war oder nicht.

app.post('/api/messages', (req, res) => {
  res.header('Content-Type', 'application/json');
  client.messages
    .create({
      from: process.env.TWILIO_PHONE_NUMBER,
      to: req.body.to,
      body: req.body.body
    })
    .then(() => {
      res.send(JSON.stringify({ success: true }));
    })
    .catch(err => {
      console.log(err);
      res.send(JSON.stringify({ success: false }));
    });
});

Das ist alles, was wir auf dem Server brauchen. Wenden wir uns nun dem React-Teil zu.

Erstellen der Clientseite

Auf der Clientseite können wir das Formular zum Senden der SMS über den Server in nur einer Komponente komplett einschließen. Wir erstellen also im src-Verzeichnis eine neue Komponente mit dem Namen SMSForm.js und beginnen mit dem Textbaustein für eine Komponente:

import React, { Component } from 'react';

class SMSForm extends Component {

}

export default SMSForm;

Wir erstellen ein Formular, in das der Benutzer eine Telefonnummer und eine Nachricht eingibt. Wenn das Formular übermittelt wird, werden die Details an unseren Server-Endpunkt und die Nachricht als SMS an die Nummer gesendet.

Wir erstellen zuerst die render-Methode für diese Komponente: Diese enthält ein Formular, eine Eingabe für die Telefonnummer, einen Textbereich für die Nachricht und eine Schaltfläche zum Übermitteln des Formulars:

  render() {
    return (
      <form>
        <div>
          <label htmlFor="to">To:</label>
          <input
             type="tel"
             name="to"
             id="to"
          />
        </div>
        <div>
          <label htmlFor="body">Body:</label>
          <textarea name="body" id="body"/>
        </div>
        <button type="submit">
          Send message
        </button>
      </form>
    );
  }

Wir können ein CSS hinzufügen, um das Formular etwas ansprechender zu gestalten. Erstelle die Datei src/SMSForm.css und füge ihr Folgendes hinzu:

.sms-form {
  text-align: left;
  padding: 1em;
}
.sms-form label {
  display: block;
}
.sms-form input,
.sms-form textarea {
  font-size: 1em;
  width: 100%;
  box-sizing: border-box;
}
.sms-form div {
  margin-bottom: 0.5em;
}
.sms-form button {
  font-size: 1em;
  width: 100%;
}
.sms-form.error {
  outline: 2px solid #f00;
}

Das CSS importieren wir am Anfang der SMSForm-Komponente:

import React, { Component } from 'react';
import './SMSForm.css';

Importiere jetzt die Komponente in die src/App.js und ersetze die Render-Methode durch Folgendes:

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import SMSForm from './SMSForm';

class App extends Component {
  render() {
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />

          <SMSForm />
        </header>
      </div>
    );
  }
}

export default App;

Wenn du jetzt die Anwendung mit npm run dev startest, siehst du das Formular auf der Seite.

react app

Das Formular hat noch keine Funktion, aber darum kümmern wir uns gleich.

Erstellen eines interaktiven Formulars in React

Um das HTML-Formular mit der Komponente zu verbinden, müssen wir noch ein paar Aufgaben durchführen:

  • Aktualisierung des Status der Komponente mit dem Status der Eingabe und des Textbereichs
  • Verarbeiten der Formularübermittlung und des Sendens der Daten an den Server
  • Verarbeiten der Antwort vom Server und Löschen des Formulars bei erfolgreichem Senden der Nachricht oder Anzeige eines Fehlers bei Fehlschlagen

Wir richten zuerst einen Anfangsstatus im Konstruktor ein. Wir müssen die Formulareingaben speichern, egal ob das Formular gerade übermittelt wird (damit wir die Senden-Schaltfläche deaktivieren können) oder ob ein Fehler aufgetreten ist. Wir erstellen den Konstruktor für die Komponente folgendermaßen:

class SMSForm extends Component {
  constructor(props) {
    super(props);
    this.state = {
      message: {
        to: '',
        body: ''
      },
      submitting: false,
      error: false
    };
  }

  // rest of the component
}

Wir benötigen eine Methode, die Änderungen in den Formularfeldern verarbeitet und den Status aktualisiert. Wir könnten zwei Methoden erstellen, eine für die Eingabe und eine für den Textbereich. Aber da die Namen der Formularelemente und die Elemente im Status übereinstimmen, können wir für beides eine Methode erstellen.

  onHandleChange(event) {
    const name = event.target.getAttribute('name');
    this.setState({
      message: { ...this.state.message, [name]: event.target.value }
    });
  }

Hier gilt zu beachten, dass wir die berechneten Eigenschaftennamen von ES2015 verwenden, um die richtige Eigenschaft im Status festzulegen, und den Verteilungsoperator, um den Rest des Status auszufüllen.

Wir müssen diese Methode an das Objekt binden, um sicherzustellen, dass this richtig ist, wenn wir es zum Empfangen eines Ereignisses verwenden. Am Ende des Konstruktors fügen wir Folgendes hinzu:

  constructor(props) {
    super(props);
    this.state = {
      message: {
        to: '',
        body: ''
      },
      submitting: false,
      error: false
    };
    this.onHandleChange = this.onHandleChange.bind(this);
  }

Jetzt können wir unsere gerenderte JSX so aktualisieren, dass der Wert der Formularfelder anhand des aktuellen Status festgelegt wird und dass Aktualisierungen mit der onHandleChange-Methode verarbeitet werden:

  render() {
    return (
      <form>
        <div>
          <label htmlFor="to">To:</label>
          <input
            type="tel"
            name="to"
            id="to"
            value={this.state.message.to}
            onChange={this.onHandleChange}
          />
        </div>
        <div>
          <label htmlFor="body">Body:</label>
          <textarea
            name="body"
            id="body"
            value={this.state.message.body}
            onChange={this.onHandleChange}
          />
        </div>
        <button type="submit">Send message</button>
      </form>
    );
  }

Lade die App jetzt neu, um die Formularfelder zu aktualisieren. Wenn wir über die React-Entwicklertools für unseren Browser verfügen, können wir auch die Aktualisierung des Status sehen.

devtools

Jetzt müssen wir uns um die Formularübermittlung kümmern. Erstelle eine weitere Funktion (onSubmit), die zuerst die submitting-Statuseigenschaft auf „true“ aktualisiert. Sende dann mithilfe der fetch-API die Anfrage an den Server. Wenn die Antwort erfolgreich ist, lösche das Formular und setze submitting auf „false“. Wenn die Antwort nicht erfolgreich ist, setze submitting auf „false“ und error auf „true“.

  onSubmit(event) {
    event.preventDefault();
    this.setState({ submitting: true });
    fetch('/api/messages', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(this.state.message)
    })
      .then(res => res.json())
      .then(data => {
        if (data.success) {
          this.setState({
            error: false,
            submitting: false,
            message: {
              to: '',
              body: ''
            }
          });
        } else {
          this.setState({
            error: true,
            submitting: false
          });
        }
      });
  }

Genau wie die onHandleChange-Methode binden wir auch diese Methode im Konstruktor:

  constructor(props) {
    super(props);
    this.state = {
      message: {
        to: '',
        body: ''
      },
      submitting: false,
      error: false
    };
    this.onHandleChange = this.onHandleChange.bind(this);
    this.onSubmit = this.onSubmit.bind(this);
  }

In der JSX fügen wir jetzt die onSubmit-Methode als Übermittlungshandler auf dem Formular hinzu. Außerdem setzen wir die Klasse des Formulars auf „error“, wenn wir von der Anfrage einen Fehler zurückerhalten. Und während das Formular übermittelt wird, legen wir die disabled-Eigenschaft für die Schaltfläche fest.

  render() {
    return (
      <form
        onSubmit={this.onSubmit}
        className={this.state.error ? 'error sms-form' : 'sms-form'}
      >
        <div>
          <label htmlFor="to">To:</label>
          <input
            type="tel"
            name="to"
            id="to"
            value={this.state.message.to}
            onChange={this.onHandleChange}
          />
        </div>
        <div>
          <label htmlFor="body">Body:</label>
          <textarea
            name="body"
            id="body"
            value={this.state.message.body}
            onChange={this.onHandleChange}
          />
        </div>
        <button type="submit" disabled={this.state.submitting}>
          Send message
        </button>
       </form>
    );
  }

Das ist auch schon alles. Aktualisiere die App erneut und gib eine Mobiltelefonnummer und eine Nachricht ein, die gesendet werden soll. Dann übermittele das Formular und wenn die Details korrekt sind, wird die Nachricht gesendet. Falls sie nicht korrekt sind, zeigt das Formular einen Fehlerstatus an.

screenshot

Senden von Nachrichten und Schutz der Anmeldeinformationen

Das Senden von SMS-Nachrichten über eine Web-App ist eine tolle Sache. Das Senden von SMS-Nachrichten über eine React-App, ohne unsere Anmeldeinformationen preiszugeben, ist noch viel toller.

Den gesamten Code für diese Beispielanwendung findest du in diesem GitHub-Repository.

Da wir uns die Grundlagen für eine React-App angeeignet haben, die SMS-Nachrichten senden kann, können wir einige Verbesserungen vornehmen. Das Erste wären vermutlich eine bessere Überprüfung und bessere Fehlermeldungen. Mit einem ähnlichen Design könnten wir außerdem direkt in der React-App eine Telefonnummernsuche hinzufügen, Telefonanrufe erzeugen oder die Zwei-Faktor-Authentisierung implementieren.

Ich würde gerne mehr über die App erfahren, die du mit React erstellst. Hinterlasse mir unten einen Kommentar, kontaktiere mich auf Twitter unter @philnash oder sende eine E-Mail an philnash@twilio.com.