Bienvenue à Chanter avec Sinatra! Dans cette troisième et dernière partie, nous étendrons l'application "Recall" que nous avons construite dans la leçon précédente. Nous allons ajouter un flux RSS à l'application avec la gemme Builder incroyablement utile, ce qui rend la création de fichiers XML dans Ruby un jeu d'enfant. Nous allons apprendre à quel point Sinatra permet d'échapper facilement au code HTML des entrées utilisateur pour empêcher les attaques XSS, et nous améliorerons certains des codes de traitement des erreurs..
La règle générale lors de la création d'applications Web est d'être paranoïaque. Paranoïde, chacun de vos utilisateurs cherche à vous attirer en détruisant votre site ou en attaquant d'autres utilisateurs à travers celui-ci. Dans votre application, essayez d'ajouter une nouvelle note avec le contenu suivant:
fous
Actuellement, nos utilisateurs sont libres d'entrer le code HTML qu'ils souhaitent. Cela laisse l'application ouverte aux attaques XSS où un utilisateur peut entrer du code JavaScript malicieux pour attaquer ou mal diriger d'autres utilisateurs du site. Donc, la première chose à faire est d'extraire tout le contenu soumis par l'utilisateur pour que le code ci-dessus soit converti en entités HTML, comme ceci:
fous
Pour ce faire, ajoutez le bloc de code suivant à votre rappel.rb
fichier, par exemple sous le DataMapper.auto_upgrade!
ligne:
les aides incluent Rack :: Utils alias_method: h,: escape_html end
Cela inclut un ensemble de méthodes fournies par Rack. Nous avons maintenant accès à un h ()
méthode pour échapper à HTML.
Pour échapper au HTML sur la page d’accueil, ouvrez le views / home.erb
voir le fichier et changer le <%= note.content %>
ligne (ligne 11) à:
<%=h note.content %>
Sinon, nous aurions pu écrire ceci comme <%= h(note.content) %>
, mais le style ci-dessus est beaucoup plus commun dans la communauté Ruby. Actualisez la page et le code HTML soumis doit maintenant être échappé et non exécuté par le navigateur:
Cliquez sur le lien "modifier" pour la note avec le code XSS, et vous penserez peut-être que c'est sûr - tout se trouve dans une zone de texte et ne s'exécute pas. Mais que se passe-t-il si nous ajoutons une nouvelle note avec le contenu suivant:
Jetez un coup d’œil à sa page d’édition et vous pouvez voir que nous avons fermé la zone de texte et que l’alerte JavaScript est exécutée. Nous devons donc clairement échapper au contenu de la note sur chaque page où elle est affichée..
À l'intérieur de votre views / edit.erb
afficher le fichier, échapper au contenu de la zone de texte
en le lançant à travers le h
méthode (ligne 4):
Et fais de même dans ton views / delete.erb
déposer sur la ligne 2:
Êtes-vous sûr de vouloir supprimer la note suivante: "<%=h @note.content %>"?
Voilà, nous sommes à l'abri de XSS. N'oubliez pas d'échapper à toutes les données soumises par les utilisateurs lors de la création d'autres applications Web à l'avenir.!
Vous vous demandez peut-être "qu’en est-il des injections SQL?" Eh bien, DataMapper le gère pour nous tant que nous utilisons les méthodes de DataMapper pour extraire des données de la base de données (c’est-à-dire ne pas exécuter de SQL brut).
Une partie importante de tout site Web dynamique est une forme de flux RSS, et notre application Recall ne fera pas exception à la règle! Heureusement c'est incroyablement facile à créer des flux grâce à la gem Builder. Installez-le avec:
gem install constructeur
Selon la configuration de RubyGems sur votre système, vous devrez peut-être préfixer bijou installer
avec sudo
.
Maintenant, ajoutez un nouvel itinéraire à votre rappel.rb
fichier de demande pour une demande GET à /rss.xml
:
get '/rss.xml' do @notes = Note.all: order =>: id.desc constructeur: rss end
Assurez-vous d'ajouter cette route quelque part au dessus de la get '/: id'
route, sinon une demande de rss.xml
serait confondu avec un post ID!
Dans l’itinéraire, nous demandons simplement toutes les notes de la base de données et nous chargeons un fichier. rss.builder
Voir la fiche. Notez comment nous utilisions auparavant le moteur ERB pour afficher un .erb
fichier, nous utilisons maintenant Builder pour traiter un fichier. Un fichier Builder est principalement un fichier Ruby normal avec une xml
objet pour créer des balises XML.
Commencez votre views / rss.builder
afficher le fichier avec les éléments suivants:
xml.instruct! : .xml,: version => "1.0" xml.rss: version => "2.0" do xml.channel do end end
Note très importante: Sur la première seconde du bloc de code ci-dessus, supprimez le point (.
) dans le texte : .xml
. WordPress interfère avec des extraits de code.
Builder analysera ceci comme étant:
Nous avons donc commencé par créer la structure d'un fichier XML valide. Ajoutons maintenant des balises pour le titre du flux, une description et un lien vers le site principal. Ajouter ce qui suit à l'intérieur du xml.channel do
bloc:
xml.title "Recall" xml.description "parce que vous êtes trop occupé pour vous rappeler" xml.link request.url
Remarquez comment nous obtenons l'URL actuelle du demande
objet. Nous pourrions le coder manuellement, mais l’idée est que vous pouvez télécharger l’application n’importe où sans avoir à changer de code obscur..
Il y a un problème cependant, le lien est maintenant défini sur (par exemple) http: // localhost: 9393 / rss.xml
. Idéalement, nous souhaiterions que le lien mette vers la page d'accueil et non vers le fil. le demande
objet a aussi un path_info
méthode qui est définie sur la chaîne de route actuelle; donc dans notre cas, /rss.xml
.
Sachant cela, nous pouvons maintenant utiliser Ruby's chomp
méthode pour supprimer le chemin de la fin de l'URL. Changer la xml.link request.url
ligne à:
xml.link request.url.chomp request.path_info
Le lien dans notre fichier XML est maintenant défini sur http: // localhost: 9393
. Nous pouvons maintenant parcourir chaque note et créer un nouvel élément XML:
@ 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
Notez que sur les lignes 3 et 7, nous échappons au contenu de la note en utilisant h
, comme nous l'avons fait dans les vues principales. C’est un peu étrange d’afficher le même contenu à la fois Titre
et le la description
balises, mais nous suivons l'exemple de Twitter ici, et il n'y a pas d'autres données que nous pouvons mettre là.
Sur la ligne 6, nous convertissons la note créé à
time to RFC822, le format requis pour les durées dans les flux RSS.
Maintenant, essayez-le dans un navigateur! Aller à /rss.xml
et vos notes devraient s'afficher correctement.
Il y a un problème mineur avec notre mise en œuvre. Dans notre vue RSS, nous avons le titre et la description du site. Nous les avons aussi dans le views / layout.erb
fichier pour la partie principale du site. Mais maintenant, si nous voulions changer le nom ou la description du site, nous devons mettre à jour deux endroits différents. Une meilleure solution consisterait à définir le titre et la description dans un placer, puis les référencer à partir de là.
À l'intérieur de rappel.rb
fichier d'application, ajoutez les deux lignes suivantes en haut du fichier, directement après la exiger
instructions, pour définir deux constantes:
SITE_TITLE = "Rappelez" SITE_DESCRIPTION = "Parce que vous êtes trop occupé pour vous en rappeler"
Maintenant de retour à l'intérieur views / rss.builder
changer les lignes 4 et 5 pour:
xml.title SITE_TITLE xml.description SITE_DESCRIPTION
Et à l'intérieur views / layout.erb
changer la
Marquer sur la ligne 5 à:
<%= "#@title | #SITE_TITLE" %>
Et changer le h1
et h2
balises de titre sur les lignes 12 et 13 à:
<%= SITE_TITLE %>
<%= SITE_DESCRIPTION %>
Nous devrions également inclure un lien vers le flux RSS dans le tête
de la page afin que les navigateurs puissent afficher un bouton RSS dans la barre d’adresse. Ajoutez ce qui suit directement avant le étiquette:
Nous avons besoin d’un moyen d’informer l’utilisateur lorsque quelque chose se passe mal ou correctement, comme un message de confirmation lorsqu’une nouvelle note est ajoutée, une note supprimée, etc..
Le moyen le plus courant et le plus logique d’atteindre cet objectif consiste à utiliser des "messages flash" - un message court ajouté à la session du navigateur de l’utilisateur, qui est affiché et effacé à la page suivante qu’ils consultent. Et il se trouve que quelques RubyGems peuvent aider à atteindre cet objectif! Entrez les informations suivantes dans le terminal pour installer le Rack Flash et la redirection Sinatra avec des gemmes Flash:
gem installer rack-flash sinatra-redirect-with-flash
Selon la configuration de RubyGems sur votre système, vous devrez peut-être préfixer bijou installer
avec sudo
.
Exigez les gemmes et activez leur fonctionnalité en ajoutant ce qui suit près du sommet de votre rappel.rb
dossier de candidature:
nécessite 'rack-flash' nécessite 'sinatra / redirect_with_flash' activer: les sessions utilisent Rack :: Flash,: sweep => true
Ajouter un nouveau message flash est aussi simple que flash [: error] = "Quelque chose s'est mal passé!"
. Affiche une erreur sur la page d'accueil lorsqu'il n'y a pas de notes dans la base de données.
Change ton obtenir '/'
Route vers:
get '/' do @notes = Note.all: order =>: id.desc @title = 'Toutes les notes' if @ notes.empty? flash [: error] = 'Aucune note trouvée. Ajoutez votre premier ci-dessous. fin erb: fin de la maison
Très simple. Si la @Remarques
la variable d'instance est vide, créez une nouvelle erreur flash. Pour afficher ces messages flash sur la page, ajoutez ce qui suit à votre views / layout.erb
fichier, avant le <%= yield %>
:
<% if flash[:notice] %><%= flash[:notice] %> <% end %> <% if flash[:error] %>
<%= flash[:error] %> <% end %>
Et ajoutez les styles suivants à votre public / style.css
fichier pour afficher les notices en vert et les erreurs en rouge:
.remarque couleur: vert; .error color: red;
À présent, votre page d'accueil devrait afficher le message "Aucune note trouvée" lorsque la base de données est vide:
Maintenant, affichons un message d'erreur ou de succès selon qu'une nouvelle note peut être ajoutée à la base de données. Change ton poste '/'
Route vers:
post '/' do n = Note.new n.content = params [: content] n.created_at = Time.now n.updated_at = Time.now si n.save redirect '/:, notice =>' Note créée avec succès ' else redirect '/',: error => 'Echec de l'enregistrement de la note.' fin fin
Le code est assez logique. Si la note peut être sauvegardée, redirigez-la vers la page d'accueil avec un message flash 'notice', sinon, redirigez vers la maison avec un message d'erreur. Ici, vous pouvez voir la syntaxe alternative pour définir un message flash et rediriger la page proposée par le gem Sinatra-Redirect-With-Flash..
L'idéal serait également d'afficher une erreur sur la page 'éditer note' si la note demandée n'existe pas. Changer la get '/: id'
Route vers:
get '/: id' do @note = Note.get params [: id] @title = "Modifier la note ## params [: id]" "si @note erb: éditer une autre redirection" / ',: error => "Vous ne trouvez pas cette note." fin fin
Et aussi sur la page de demande PUT pour mettre à jour une note. Changement mettre '/: id'
à:
put '/: id' do n = Note.get params [: id] sauf si n redirige '/',: error => "Vous ne trouvez pas cette note." end n.content = params [: content] n.complete = params [: complete]? 1: 0 n.updated_at = Time.now if n.save redirect '/',: notice => 'Remarque mise à jour avec succès.' else redirect '/',: error => 'Erreur lors de la mise à jour de la note.' fin fin
Changer la get '/: id / delete'
Route vers:
get '/: id / delete' do @ note = Note.get params [: id] @title = "Confirmer la suppression de la note ## params [: id]" "si @note erb: éditer une autre redirection '/', : error => "Vous ne trouvez pas cette note." fin fin
Et sa demande DELETE correspondante, supprimer '/: id'
à:
delete '/: id' do n = Note.get params [: id] si n.destroy redirect '/',: notice => 'Remarque supprimée avec succès.' else redirect '/',: error => 'Erreur lors de la suppression de la note.' fin fin
Enfin, changez le get '/: id / complete'
route vers les sites suivants:
get '/: id / complete' do n = Note.get paramètres [: id] sauf si n redirige '/',: error => "Vous ne trouvez pas cette note." end n.complete = n.complete? 0: 1 # retournez n.updated_at = Time.now si n.save redirect '/',: notice => 'Note marquée comme complète.' else redirect '/',: error => 'Erreur de marquage de la note comme complète.' fin fin
Une application Web fonctionnelle, sécurisée et sensible aux erreurs, écrite avec une quantité de code étonnamment petite! Au cours de cette courte mini-série, nous avons appris à traiter diverses requêtes HTTP avec une interface RESTful, à gérer les envois de formulaires, à échapper à un contenu potentiellement dangereux, à vous connecter à une base de données, à travailler avec les sessions utilisateur pour afficher des messages flash, à générer un flux RSS dynamique et comment gérer avec élégance les erreurs d'application.
Si vous souhaitez approfondir l'application, vous pouvez vous pencher sur le traitement de l'authentification d'utilisateur, comme avec le gem Authentification de Sinatra..
Si vous souhaitez déployer l'application sur un serveur Web, Sinatra étant construit avec Rake, vous pouvez très facilement héberger vos applications Sinatra sur des serveurs Apache et Nginx en installant Passenger..
Découvrez également Heroku, une plate-forme d’hébergement optimisée par Git qui simplifie le déploiement de vos applications Web Ruby. Git Push Heroku
(des comptes gratuits sont disponibles!)
Si vous souhaitez en savoir plus sur Sinatra, consultez le très détaillé Readme, les pages de documentation et le livre gratuit de Sinatra..
Remarque: les fichiers source de chaque partie de cette mini-série sont disponibles sur GitHub, ainsi que l'application finie.