Mailpen

Entwicklung einer Schnittstelle zwischen Front- und Backend

Mischa Helfenstein
Abschlussprüfung Winter 2015

Inhaltsverzeichnis

suwepro GmbH

  • Gegründet: 2011
  • 4 Mitarbeiter
  • Werbe- und IT-Service
  • Tätigkeiten:
    • Webdesign
    • Printdesign
    • IT-Service
    • Außendarstellung

Projekt - Mailpen

  • Newsletterservice
  • Teilprojekt
  • Schnittstelle zwischen Front- und Backend
  • Darstellung und Server-Logik vollständig getrennt
  • Ziel: Mögliche Integration in andere Systeme

Projektbegründung

  • Altes System
  • fehleranfällig
  • System von Fremdanbieter
  • wenig Kontrolle
  • Lange Wartezeiten für Kunden

Projektziel

  • Selbstpflege durch den Kunden
  • Integration in Systeme der Kunden
  • Kürzere Wartezeiten
  • Geringerer Aufwand

System

Backend:
  • PHP mit SLIM-Framework
  • Ermöglicht einfachen Aufbau von REST-APIs
Frontend:
  • Web-Applikation mit AngularJS
  • SinglePage App
  • Darstellung auf mehreren Geräten
Authentifizierung:
  • Konzeptionsphase

Zeitplanung

Projektphase Geplante Zeit
Planungsphase 2 h
Konzeptionsphase 7 h
Implementierungsphase 38 h
Testphase 6 h
Dokumentationsphase 17 h
Gesamt: 70 h

Vorgehensmodell

Iteratives Wasserfallmodell
Phasen:
  1. Systemanalyse
  2. Software - Spezifikationen
  3. Architekturentwurf
  4. Kodierung
  5. Test
  6. Installation und Abnahme
  7. Betrieb und Wartung

Ist-Zustand

  • Eingerichtete GIT-Code-Verwaltung
  • Relationen-Modell
  • Grundlage der API
  • Datenbank bereits entworfen und umgesetzt

Wirtschaftlichkeit

Kosten
Beschreibung Kosten
Entwicklung 840 €
Abnahme 30 €
Gesamt: 870 €
Ersparnis
Art Ersparnis
Service 300 €/J
Personal 762 €/J
Gesamt: 1062 €/J
Amortisation
9 Monate 24 Tage

Authentifizierung

Cookie / Session
Problem: Unterstüzung unterschiedlicher Systeme
Übertragen bei jeder Anmeldung
Problem: Sicherheit
JSON-Web-Token (JWT)
Einfache Verarbeitung
Schnelle Überprüfung (Signatur)
Lösung!

API-Design

  • Auf Basis des Relationenmodells
  • REST-Schnittstelle
  • JSON Format
  • Verschiedene Möglichkeiten: GET, POST, PUT, DELETE
  • Beispiel:
    GET api.mailpen.de/list/:listId/subscribers

API-Routen


// Group: list
$app->group('/list', $checkAuthentication, function () use ($app, $checkList) {

  // Route: list info
  $app->get('/:listId/', $checkList, function ($listId) use ($app) {
    echo "List info";
  });

  // Route: list change
  $app->put('/:listId/', $checkList, function ($listId) use ($app) {
    echo "List change";
  });

  [...]

});
						

API-Methoden

  • Klassen für Objekte
  • Middleware zur Absicherung/Kontrolle
  • Einbindung in die API-Routen

API-Methoden Klassen


class NLList {
  // .. Variablen
  // Konstruktor
  public function __construct ($listId = null) {
    $this->_db = DB::getInstance();
    if ($listId) $this->find($listId);
  }
  // Liste suchen
  public function find ($listId) {
    // check id
    if (is_numeric($listId)) {
      // fetching data
      $listData = $this->_db->get(Config::get('db_tables/lists'), array('id', '=', $listId));
      // check if there is any data
      if ($listData->count()) {
        [...]

API-Methoden Middleware


$checkList = function ($route) use ($app) {
  // get listId
  $listId = $route->getParams()['listId'];
  // create instance of list
  $list = new NLList($listId);
  // check if list exists
  if ($list->exists()) {
    // fetch data
    $listData = $list->getData();
    if ($listData['user_id'] != $app->loginData->id) {
      // no permission to access that info
      $errorMsg = array('ErrorMsg' => 'No permission');
      $app->halt(401, json_encode($errorMsg));
    }
  } else { [...]
  }
};
							

API-Methoden Integration


$app->delete('/:listId/', $checkList, function ($listId) use ($app) {
  // create instance
  $list = new NLList($listId);
  // try deleting list
  try {
    $list->delete();
  } catch (Exception $e) {
    // cannot delete
    $errorMsg = array(
        'ErrorMsg' => 'Cannot delete list',
        'Exception' => $e
    );
    $app->halt(500, json_encode($errorMsg));
  }
});
							

Angular-JS Anbindung


function getList(listId) {
  // handle success
  function getListSuccess(response) {
    return response;
  }
  // handle failure
  function getListFailed(response) {
    // Log error
    logError(response.status, 'Loading list - failed', response.data);
    // return response
    return response;
  }
  // request
  return $http.get(apiUrl + '/list/' + listId, getAuthHeaders())
    .then(getListSuccess)
    .catch(getListFailed);
}
						

Probleme

  • Probleme mit CORS (Cross-Origin Resource Sharing)
  • Fehler:
    Origin <Domain> is not allowed by Access-Control-Allow-Origin        
  • Preflight Anfragen werden nicht beantwortet

Lösung

Beantwortung der Preflight Anfragen

if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
  // return only the headers and not the content
  if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD'])) {
    header('Access-Control-Allow-Origin: *');
    header('Access-Control-Allow-Headers: x-requested-with');
  }
  exit;
}
							

Backend - Testing

  • PAW REST-Client (Mac)
  • Umgebungsvariablen
  • Visualisierte Fehlercodes
  • JSON-Ausgabe (Formatierung)

Frontend - Testing

  • Einfacher Testcontroller
  • Funktion der API bereits geprüft
  • Test der Anfragen
  • Ausgabe auf der Konsole

Abschluss

Fazit

  • Erfolgreich
  • Lessons-Learned
    • Behandeln von unerwarteten Problemen
    • Recherche von Fehlern
  • Zeit eingehalten jedoch Anpassungen zwischen Phasen

Quellen

  • https://auth0.com/blog/2014/01/07/angularjs-authentication-with-cookies-vs-token/

Vielen Dank für Ihre Aufmerksamkeit!