Meteor - Teil 2

Meteor – Teil 2

Von am 23.04.2015

Im Artikel „Meteor – Open Source Plattform für Web-Apps und Mobile-Apps“ gab es bereits einen Überblick und Einstieg zu Meteor. Ich habe mich im Wintersemester 2014 noch weiter mit Meteor beschäftigt und werde in diesem Artikel darauf eingehen.

Ordnerstruktur

Grundsätzlich ist die Ordnerstruktur frei wählbar. Bereits im ersten Artikel wurde eine Startstruktur vorgestellt, die auch in der Meteor Dokumentation vorgeschlagen wird. Diese Struktur habe ich auch beibehalten und man kann damit gut arbeiten. Wenn man ein größeres Projekt plant, sollte man vielleicht genauer darüber nachdenken (z.B. Ordern “views”, “models”, etc.). Es gibt dazu verschiedene Ansätze im Web.

  • client
  •     css
  •     js
  • lib
  • private
  • public
  •      img
  • server

Im „client“ Ordner habe ich noch zwei Unterordner („css“ und „js“) angelegt. Direkt im Client-Ordner befinden sich meine HTML-Templates, im JS-Ordner die dazugehörigen JavaScript-Dateien. Der CSS-Ordner enthält alle CSS-Dateien. Diese kann man auch wieder nach belieben organisieren, da gibt es verschiedene Ansätze (SMACSS, BEM, OOCSS, etc.).

Der Lib-Ordner (Zugriff von Server und Client) enthält eine Datei, die Collections von MongoDB lädt. Außerdem eine Datei „routes.js“, dazu unten mehr.

Der Private-Ordner ist derzeit noch leer (hier kommen Dateien hin, die nur vom Server gelesen werden können). Im Public-Ordner habe ich einen Unterordner „img“ angelegt, der meine Bilder enthält, die ich für das Layout (z.B. Logo, Sprites, etc.) verwende.

Im Server-Ordner gibt es eine Datei „methods.js“. Diese stellt Methoden für den Datenbankzugriff zur Verfügung und ist nur vom Server aus erreichbar. Genauso wie die Datei „publish.js“, die festlegt, welche Daten von der Datenbank zum Client geschickt werden.

Servermethoden und Sicherheit

Grundsätzlich kann jeder Benutzer alles in der Datenbank verändern. Um das zu verhindern, ist es am Besten, wenn man Servermethoden definiert. Also statt direkt am Client die Datenbankoperationen aufzurufen (Insert, Update, Remove, etc.) erstellt man Servermethoden. In diesen kann man dann z.B. prüfen, ob die Daten korrekt sind oder der User auch wirklich autorisiert ist die Datenbankoperationen auszuführen.

Zuerst muss man das Package „insecure“ entfernen:

meteor remove insecure

Dieses Packet erlaub es die Daten vom Client aus zu ändern. Das mag für Prototyping sinnvoll sein, aber nicht für echte Anwendungen. Nach dem entfernen des Packets funktionieren keine Datenbankänderungen mehr vom Client aus. Jetzt muss man Servermethoden verwenden.
Das schaut zum Beispiel so aus:

// „server/methods.js“

Meteor.methods({
    /**
     * Add record
     *
     * @param int id Id of record
     * @param boolean status New status, true or false
     * @return boolean True for success, otherwise false
     */
    updateActive: function(theId, setStatus) {
        if (!Meteor.userId())
            throw new Meteor.Error('not-authorized');

        return Record.update(theId, { $set: { status: setStatus } });
    },
});

Am client ruft man jetzt die Funktion auf:

Meteor.call(‚updateActive’, 1, false);

Damit lässt sich der Datenbankzugriff einschränken. Außerdem hat man einen weitern Vorteil der sich „Latency Compensation“ nennt. Während der Client den Request sendet, läuft eine Simulation direkt am Client die das Ergebnis anwendet. Wenn ein Ok vom Server zurück kommt, bleibt alles wie es ist, wenn nicht, wird das Resultat rückgängig gemacht.

Publish und Subscribe

Mit „Publish“ und „Subscribe“ gibt es eine weitere Sicherheitskomponente. Um nicht alle Daten an den Client zu schicken (z.B. private Benutzerdaten) wird zuerst das Packet „autopublish“ entfernt:

meteor remove  autopublish

Jetzt kann man im Ordner „server“ eine Datei „publish.js“ anlegen. Hier wird definiert, welche Daten zum Client geschickt werden sollen.

// „server/publish.js“

/**
* Publish user data
*
* @return object User
*/
Meteor.publish('userdata', function() {
    return Events.find({
        owner: this.userId
    });
});

Das vorherige Beispiel zeigt, wie man z.B. nur die Daten des aktuellen Benutzers zurückliefern kann, damit dieser Benutzer nicht andere Benutzerdaten einsehen kann. Am Client muss dann noch die Methode

Meteor.subscribe(‚userdata);

aufgerufen werden. Wenn man dann die Collection lädt und abruft, erhält man am Client nur noch die Daten des aktuellen Users.

Data = new Mongo.Collection(‚userdata);

var d = Data.find();

Die Variable „d“ enthält jetzt nur alle Daten bei denen die Eigenschaft „owner“ (Id des Benutzers), der des aktuellen Benutzers entspricht.

Iron Router

In Meteor kann man zwar verschiedene Templates dynamisch laden, wenn man jedoch separate Seiten mit einer eindeutigen URL erstellen will, stößt man schnell auf „Iron Router“.

Iron Router ist zwar nicht in Meteor enthalten, lässt sich aber einfach nachinstallieren und ist quasi der Standard-Router bei Meteor-Projekten.

meteor add iron-router

Mit Iron Router lassen sich Routen definieren und je nach Route verschiedene Templates rendern. Außerdem kann beim Routing schon bestimmt werden, welche Daten dem Template zur Verfügung gestellt werden und was angezeigt werden soll während die Daten laden.

Um das ganze dynamisch zu gestalten, kann man auch eine Layout-Template wählen in welches die Routen (an der Stelle „{{> yield}}“) gerendert werden. Außerdem können spezielle Regionen definiert werden, wie im folgenden Code („header“), der es erlaubt verschiedene Header zu rendern.

<!-- „client/layout.html“ -->

<head>
    <title>App Title</title>
</head>

<template name="layout">
    <header>
        {{> yield "header"}}
    </header>

    <main role="main">
        {{> yield}}
    </main>

    <footer>
        Footer
    </footer>
</template>
// „lib/routes.js“

/**
 * Router config
 */
Router.configure({
    layoutTemplate: 'layout' // Datei „layout.html“ im Ordner „client“
});

/**
 * Define routes
 */
Router.map(function() {
    /** Index route for all */
    this.route('index', {
        path: '/',
        action: function() {
            this.render('header', { to: 'header' }); // Template mit Namen „header“
            this.render('index'); // Template mit Namen „indes“
        }
    });

    /** About route */
    this.route('about’, {
        path: '/about,
        action: function() {
            this.render('header', { to: 'header' });
            this.render('about');
        }
    });
});

Das Beispiel zeigt ein einfaches Routing mit zwei Routen. Wenn man keine Action oder sonstiges hat, kann man auch z.B. einfach nur `this.route(‚contact’);` schreiben, dann wird beim Aufruf von „/contact“ das Template „contact“ gerendert.

Mehr zu Iron Router:

Up Next

Im nächsten Artikel werde ich auf User Accounts und Templates (Verarbeitung von Formulardaten, Context, Helpers) eingehen.

The comments are closed.