Mit Sinatra singen - The Encore

Willkommen zurück bei Sinatra! In diesem dritten und letzten Teil erweitern wir die App "Recall", die wir in der vorherigen Lektion erstellt haben. Wir werden der App einen RSS-Feed mit dem unglaublich nützlichen Builder-Edelstein hinzufügen, der das Erstellen von XML-Dateien in Ruby zum Kinderspiel macht. Wir lernen, wie einfach Sinatra es ermöglicht, HTML von Benutzereingaben zu entgehen, um XSS-Angriffe zu verhindern, und wir verbessern den Code für die Fehlerbehandlung.


Benutzer sind schlecht, m'kay

Die allgemeine Regel beim Erstellen von Web-Apps ist paranoid. Paranoid, dass jeder Ihrer Benutzer darauf aus ist, Sie zu vernichten, indem Sie Ihre Website zerstören oder andere Benutzer über sie angreifen. Fügen Sie in Ihrer App eine neue Notiz mit folgendem Inhalt hinzu:

 woops 

Derzeit können unsere Benutzer beliebiges HTML eingeben. Dies lässt die App offen für XSS-Angriffe, bei denen ein Benutzer schädliches JavaScript eingeben kann, um andere Benutzer der Website anzugreifen oder fehlzuleiten. Das erste, was wir tun müssen, ist escce alle vom Benutzer übermittelten Inhalte, so dass der obige Code in HTML-Entitäten konvertiert wird, wie dies z.

 woops 

Fügen Sie dazu den folgenden Code-Block hinzu Rückruf.rb Datei, zum Beispiel unter der DataMapper.auto_upgrade! Linie:

 Helfer enthalten Rack :: Utils alias_method: h,: escape_html end

Dazu gehört eine Reihe von Methoden, die von Rack bereitgestellt werden. Wir haben jetzt Zugriff auf eine h () Methode, um HTML zu umgehen.

Um HTML auf der Homepage zu umgehen, öffnen Sie die Ansichten / home.erb Datei anzeigen und ändern <%= note.content %> Zeile (um Zeile 11) nach:

 <%=h note.content %>

Alternativ hätten wir das auch als schreiben können <%= h(note.content) %>, Der obige Stil ist jedoch viel häufiger in der Ruby-Community. Aktualisieren Sie die Seite und der übergebene HTML-Code sollte jetzt mit Escapezeichen versehen und nicht vom Browser ausgeführt werden:

XSS auf den anderen Seiten

Klicken Sie auf den Link "Bearbeiten" für die Notiz mit dem XSS-Code. Vielleicht denken Sie, dass dies sicher ist - sie befinden sich alle in einem Textbereich und werden daher nicht ausgeführt. Was aber, wenn wir eine neue Notiz mit folgendem Inhalt hinzugefügt haben:

  

Werfen Sie einen Blick auf die Bearbeitungsseite, und Sie können sehen, dass wir den Textbereich geschlossen haben und die JavaScript-Warnung ausgeführt wird. Wir müssen also eindeutig den Inhalt der Notiz auf jeder Seite, auf der sie angezeigt wird, schützen.

In deinem Ansichten / edit.erb Datei anzeigen, flüchten Sie den Inhalt im Textbereich indem Sie es durch die h Methode (Zeile 4):

 

Und mache dasselbe in deinem Ansichten / delete.erb Datei in Zeile 2:

 

Möchten Sie die folgende Notiz wirklich löschen: "<%=h @note.content %>"?

Da haben Sie es - wir sind jetzt vor XSS geschützt. Denken Sie daran, alle vom Benutzer übermittelten Daten zu sichern, wenn Sie in Zukunft weitere Web-Apps erstellen!

Sie fragen sich vielleicht "Was ist mit SQL-Injektionen?" Nun, DataMapper erledigt das für uns, solange wir die DataMapper-Methoden verwenden, um Daten aus der Datenbank abzurufen (dh, es wird kein reines SQL ausgeführt)..


RSS Feed der Massen

Ein wichtiger Teil jeder dynamischen Website ist eine Form von RSS-Feed, und unsere Recall-App macht da keine Ausnahme! Zum Glück ist es so unglaublich Einfaches Erstellen von Feeds dank dem Builder-Edelstein. Installieren Sie es mit:

 gem install builder

Je nachdem, wie Sie RubyGems auf Ihrem System eingerichtet haben, müssen Sie möglicherweise ein Präfix einfügen gem installieren mit Sudo.

Fügen Sie jetzt eine neue Route zu Ihrem hinzu Rückruf.rb Anwendungsdatei für eine GET-Anfrage an /rss.xml:

 get '/rss.xml' do @notes = Note.all: order =>: id.desc builder: rss end

Stellen Sie sicher, dass Sie diese Route irgendwo hinzufügen über das '/: id' erhalten Route, sonst eine Anfrage für rss.xml würde mit einer Post-ID verwechselt werden!

In der Route fordern wir einfach alle Notizen aus der Datenbank an und laden ein rss.builder Datei ansehen. Beachten Sie, wie wir bisher die ERB-Engine zur Anzeige von a verwendet haben .erb Datei, jetzt verwenden wir Builder, um eine Datei zu verarbeiten. Eine Builder-Datei ist meistens eine normale Ruby-Datei mit einem Special xml Objekt zum Erstellen von XML-Tags.

Starte dein Ansichten / rss.builder Datei mit folgendem Inhalt anzeigen:

 xml.instruct! : .xml,: version => "1.0" xml.rss: version => "2.0" do xml.channel beendet das Ende

Sehr wichtiger Hinweis: Entfernen Sie in der ersten Sekunde des obigen Codeblocks die Periode (.) im Text : .xml. WordPress stört Codeausschnitte.

Der Builder wird dies so analysieren:

     

Wir haben also damit begonnen, die Struktur für eine gültige XML-Datei zu erstellen. Fügen wir nun Tags für den Feed-Titel, die Beschreibung und einen Link zur Hauptseite hinzu. Fügen Sie folgendes in das xml.channel Block:

 xml.title "Recall" xml.description "weil Sie zu beschäftigt sind, um sich an" xml.link request.url "zu erinnern

Beachten Sie, wie wir die aktuelle URL von erhalten anfordern Objekt. Wir könnten dies manuell eingeben, aber die Idee ist, dass Sie die App überall hochladen können, ohne obskure Teile des Codes ändern zu müssen.

Es gibt jedoch ein Problem. Der Link ist jetzt auf (zum Beispiel) gesetzt. http: // localhost: 9393 / rss.xml. Idealerweise sollte der Link zur Startseite und nicht zum Feed führen. Das anfordern Objekt hat auch eine Pfad_info Methode, die auf die aktuelle Routenzeichenfolge gesetzt ist; so in unserem Fall, /rss.xml.

Wenn wir das wissen, können wir jetzt Rubys verwenden chomp Methode, um den Pfad vom Ende der URL zu entfernen. Ändere das xml.link request.url Zeile zu:

 xml.link request.url.chomp request.path_info

Der Link in unserer XML-Datei ist jetzt auf gesetzt http: // localhost: 9393. Wir können jetzt jede Notiz durchlaufen und ein neues XML-Element dafür erstellen:

 @ notes.each do | note | xml.item do xml.title h note.content xml.link "# request.url.chomp request.path_info / # note.id" xml.guid "# request.url.chomp request.path_info / # note.id "xml.pubDate Time.parse (note.created_at.to_s) .rfc822 xml.description h note.content end end

Beachten Sie, dass in den Zeilen 3 und 7 der Inhalt der Notiz mithilfe von deaktiviert wird h, So wie wir es in den Hauptansichten gemacht haben. Es ist etwas seltsam, für beide die gleichen Inhalte anzuzeigen Titel und das Beschreibung Tags, aber wir folgen Twitter hier und es gibt keine anderen Daten, die wir dort ablegen könnten.

In Zeile 6 konvertieren wir die Note hergestellt in time to RFC822, das erforderliche Format für Zeiten in RSS-Feeds.

Probieren Sie es jetzt in einem Browser aus! Gehe zu /rss.xml und Ihre Notizen sollten korrekt angezeigt werden.


TROCKEN Wiederholen Sie sich nicht

Bei der Implementierung gibt es ein kleines Problem. In unserer RSS-Ansicht haben wir den Site-Titel und die Beschreibung. Wir haben sie auch im Ansichten / layout.erb Datei für den Hauptteil der Site. Aber wenn wir jetzt den Namen oder die Beschreibung der Site ändern wollten, gibt es zwei verschiedene Orte, die wir aktualisieren müssen. Eine bessere Lösung wäre, den Titel und die Beschreibung in einzufügen ein platzieren, dann referenzieren sie von dort.

In der Rückruf.rb Anwendungsdatei, fügen Sie die folgenden zwei Zeilen direkt am Anfang der Datei hinzu nach dem das benötigen Anweisungen, um zwei Konstanten zu definieren:

 SITE_TITLE = "Recall" SITE_DESCRIPTION = "weil Sie zu beschäftigt sind, um sich daran zu erinnern"

Nun wieder drinnen Ansichten / rss.builder Ändern Sie die Zeilen 4 und 5 in:

 xml.title SITE_TITLE xml.description SITE_DESCRIPTION

Und drinnen Ansichten / layout.erb ändere das </code> Tag 5 in Zeile:</p> <pre> <title><%= "#@title | #SITE_TITLE" %>

Und das ändern h1 und h2 Titel-Tags in den Zeilen 12 und 13 bis:

 

<%= SITE_TITLE %>

<%= SITE_DESCRIPTION %>

Wir sollten auch einen Link zum RSS-Feed in der Kopf der Seite, damit Browser eine RSS-Schaltfläche in der Adressleiste anzeigen können. Fügen Sie direkt vor dem ein Etikett:

 

Flash Messages Fehler und Erfolge

Wir brauchen eine Möglichkeit, den Benutzer zu informieren, wenn etwas schiefgelaufen ist - oder richtig, wie beispielsweise eine Bestätigungsmeldung, wenn eine neue Notiz hinzugefügt wird, eine Notiz entfernt wird usw.

Der gebräuchlichste und logischste Weg, dies zu erreichen, sind "Flash-Nachrichten" - eine kurze Nachricht, die in die Browsersitzung des Benutzers eingefügt wird. Diese Nachricht wird auf der nächsten angezeigten Seite angezeigt und gelöscht. Und zufällig gibt es ein paar RubyGems, die dazu beitragen, dies zu erreichen! Geben Sie im Terminal Folgendes ein, um Rack Flash und Sinatra Redirect mit Flash-Gems zu installieren:

 gem installieren rack-flash-sinatra-redirect-with-flash

Je nachdem, wie Sie RubyGems auf Ihrem System eingerichtet haben, müssen Sie möglicherweise ein Präfix einfügen gem installieren mit Sudo.

Fordern Sie die Edelsteine ​​an und aktivieren Sie ihre Funktionalität, indem Sie die folgenden in der Nähe Ihres hinzufügen Rückruf.rb Bewerbungsdatei:

 "Rack-Flash" erfordern "Sinatra / Redirect_with_flash" aktivieren: Sitzungen verwenden Rack :: Flash,: sweep => true

Das Hinzufügen einer neuen Flash-Nachricht ist so einfach flash [: error] = "Etwas ist schief gelaufen!". Lassen Sie uns einen Fehler auf der Homepage anzeigen, wenn keine Notizen in der Datenbank vorhanden sind.

Ändere dein erhalten '/' der Weg nach:

 get '/' do @notes = Note.all: order =>: id.desc @title = 'All Notes' wenn @ notes.empty? flash [: error] = 'Keine Notizen gefunden. Fügen Sie Ihren ersten unten hinzu. ' ende erb: heim ende

Sehr einfach. Wenn die @Anmerkungen Instanzvariable ist leer. Erstellen Sie einen neuen Flash-Fehler. Um diese Flash-Meldungen auf der Seite anzuzeigen, fügen Sie Folgendes zu Ihrer hinzu Ansichten / layout.erb Datei vor dem <%= yield %>:

 <% if flash[:notice] %> 

<%= flash[:notice] %> <% end %> <% if flash[:error] %>

<%= flash[:error] %> <% end %>

Fügen Sie die folgenden Stile hinzu public / style.css Datei, um Benachrichtigungen in grün und Fehler in rot anzuzeigen:

 .Bekanntmachung Farbe: Grün;  .error Farbe: rot; 

Jetzt sollte auf Ihrer Homepage die Meldung "Keine Notizen gefunden" angezeigt werden, wenn die Datenbank leer ist:

Lassen Sie uns nun entweder eine Fehler- oder eine Erfolgsmeldung anzeigen, je nachdem, ob der Datenbank eine neue Notiz hinzugefügt werden könnte. Ändere dein post '/' der Weg nach:

 post '/' do n = Note.newn.content = params [: content] n.created_at = Time.now n.updated_at = Time.now, wenn n.save Weiterleitung '/',: notice => 'Hinweis erfolgreich erstellt . ' else Weiterleitung '/',: error => 'Notiz konnte nicht gespeichert werden.' Ende Ende

Der Code ist ziemlich logisch. Wenn die Notiz gespeichert werden kann, leiten Sie mit einer "Hinweis" -Flashnachricht auf die Homepage um, andernfalls mit einer Fehlermeldung. Hier sehen Sie die alternative Syntax zum Setzen einer Flash-Nachricht und zum Umleiten der vom Sinatra-Redirect-With-Flash-Gem angebotenen Seite.

Es ist auch ideal, einen Fehler auf der Seite "Notiz bearbeiten" anzuzeigen, wenn die angeforderte Notiz nicht vorhanden ist. Ändere das '/: id' erhalten der Weg nach:

 get '/: id' do @note = Note.get params [: id] @title = "Notiz bearbeiten ## params [: id]" if @note erb: edit else Weiterleitung '/',: error => "Ich kann diese Notiz nicht finden." Ende Ende

Und auch auf der PUT-Anforderungsseite für das Aktualisieren einer Notiz. Veränderung set '/: id' zu:

 put '/: id' do n = Note.get params [: id] außer n redirect '/',: error => "Diese Notiz kann nicht gefunden werden." end n.content = params [: content] n.complete = params [: complete]? 1: 0 n.updated_at = Time.now, wenn n.save umleiten '/',: notice => 'Hinweis erfolgreich aktualisiert.' else Weiterleitung '/',: error => 'Fehler beim Aktualisieren der Notiz.' Ende Ende

Ändere das '/: id / delete' erhalten der Weg nach:

 get '/: id / delete' do @note = Note.get params [: id] @title = "Löschen des Hinweises ## params [: id]" bestätigen "if @note erb: edit else Weiterleitung '/', : error => "Diese Notiz kann nicht gefunden werden." Ende Ende

Und die entsprechende DELETE-Anfrage, '/: id' löschen zu:

 delete '/: id' do n = Note.get params [: id] if n.destroy umleiten '/',: notice => 'Hinweis erfolgreich gelöscht.' else Weiterleitung '/',: error => 'Fehler beim Löschen der Notiz.' Ende Ende

Ändern Sie schließlich die '/: id / complete' erhalten Route zu folgendem:

 get '/: id / complete' do n = Note.get params [: id] außer n redirect '/',: error => "Diese Notiz kann nicht gefunden werden." end n.complete = n.complete? 0: 1 # Flip it n.updated_at = Time.now wenn n.save umleiten '/',: notice => 'Hinweis als abgeschlossen markiert.' else Weiterleitung '/',: error => 'Fehler beim Markieren der Notiz als abgeschlossen.' Ende Ende

Und da hast du es!

Eine funktionierende, sichere und auf Fehler reagierende Web-App, die mit erstaunlich wenig Code geschrieben wurde! In dieser kurzen Miniserie haben wir gelernt, verschiedene HTTP-Anforderungen mit einer RESTful-Schnittstelle zu verarbeiten, Formularsendungen zu bearbeiten, potenziell gefährlichen Inhalt zu umgehen, eine Verbindung mit einer Datenbank herzustellen, mit Benutzersitzungen zu arbeiten, Flash-Nachrichten anzuzeigen, einen dynamischen RSS-Feed zu generieren und wie man Anwendungsfehler elegant behandelt.

Wenn Sie die App weiterentwickeln möchten, sollten Sie sich mit der Benutzerauthentifizierung befassen, z. B. mit dem Sinatra Authentication-Gem.

Wenn Sie die App auf einem Webserver bereitstellen möchten, da Sinatra mit Rake erstellt wurde, können Sie Ihre Sinatra-Anwendungen sehr einfach auf Apache- und Nginx-Servern hosten, indem Sie Passenger installieren.

Besuchen Sie alternativ Heroku, eine auf Git basierende Hosting-Plattform, die die Bereitstellung Ihrer Ruby-Web-Apps so einfach wie möglich macht git Push Heroku (kostenlose Konten sind verfügbar!)

Wenn Sie mehr über Sinatra erfahren möchten, lesen Sie die ausführliche Readme-Datei, die Dokumentationsseiten und das kostenlose Sinatra-Buch.

Hinweis: Die Quelldateien für jeden Teil dieser Mini-Serie sind zusammen mit der fertigen App auf GitHub verfügbar.