Sjungar med Sinatra - The Encore

Välkommen tillbaka till Singing med Sinatra! I den tredje och sista delen kommer vi att utvidga appen "Recall" som vi byggde i den tidigare lektionen. Vi kommer att lägga till ett RSS-flöde till appen med den otroligt användbara Builder-pärlan, vilket gör att skapa XML-filer i Ruby en bit tårta. Vi lär oss hur lätt Sinatra gör att man hämtar HTML från användarinmatning för att förhindra XSS-attacker, och vi förbättrar vissa av felhanteringskoden.


Användare är dåliga, m'kay

Den allmänna regeln när man bygger webbapps ska vara paranoid. Paranoid att alla dina användare är ute för att få dig genom att förstöra din webbplats eller angripa andra användare genom den. I appen kan du försöka lägga till en ny anteckning med följande innehåll:

 woops 

För närvarande är våra användare gratis att skriva in vilken HTML som helst. Detta gör att appen är öppen för XSS-attacker, där en användare kan komma in i skadlig JavaScript för att attackera eller felaktiga andra användare av webbplatsen. Så det första vi behöver göra är att escpa allt användarinlämnat innehåll så att ovanstående kod kommer att konverteras till HTML-enheter, som så:

 woops 

För att göra detta lägger du till följande kodblock till din recall.rb fil, till exempel under DataMapper.auto_upgrade! linje:

 Hjälpare inkluderar Rack :: Utils alias_method: h,: escape_html end

Detta inkluderar en uppsättning metoder som tillhandahålls av Rack. Vi har nu tillgång till a h () metod för att fly HTML.

För att fly HTML på hemsidan, öppna visningar / home.erb visa fil och ändra <%= note.content %> linje (runt linje 11) till:

 <%=h note.content %>

Alternativt kunde vi ha skrivit detta som <%= h(note.content) %>, men stilen ovan är mycket vanligare i Ruby-samhället. Uppdatera sidan och den inlämnade HTML-filen ska nu släppas, och inte exekveras av webbläsaren:

XSS på övriga sidor

Klicka på länken "Redigera" för anteckningen med XSS-koden, och du kanske tror att det är säkert - det är allt som sitter inne i en textarea, och det går inte att exekvera. Men om vi lagt till en ny anteckning med följande innehåll:

  

Ta en titt på dess redigeringssida, och du kan se att vi har stängt av textområdet och så JavaScript-varningen utförs. Så klart måste vi flyga notens innehåll på varje sida där den visas.

Inuti din visningar / edit.erb visa fil, fly innehållet inuti textarea genom att köra den genom h metod (rad 4):

 

Och gör detsamma i din visningar / delete.erb fil på rad 2:

 

Är du säker på att du vill radera följande anteckning: "<%=h @note.content %>"?

Där har du det - vi är nu säkra från XSS. Kom bara ihåg att fly undan alla användarinlämnade data när du skapar andra webbapps i framtiden!

Du kanske undrar "hur är det med SQL-injektioner?" Tja, DataMapper hanterar det för oss lika länge som vi använder DataMappers metoder för att hämta data från databasen (dvs inte utföra rå SQL).


RSS Feed massorna

En viktig del av en dynamisk webbplats är någon form av RSS-flöde, och vår Recall app kommer inte att bli något undantag! Tack och lov är det oerhört Lätt att skapa feeds tack vare Builder-pärlan. Installera det med:

 gem install byggare

Beroende på hur du har skapat RubyGems på ditt system, kan du behöva prefix gem installation med sudo.

Lägg nu till en ny rutt till din recall.rb ansökningsfil för en GET-förfrågan till /rss.xml:

 få '/rss.xml' göra @notes = Note.all: order =>: id.desc builder: rss end

Se till att du lägger till denna rutt någonstans ovan de få '/: id' rutt, annars en begäran om rss.xml skulle misstas för ett post-ID!

I rutten begär vi helt enkelt alla anteckningar från databasen och laddar en rss.builder visa fil. Observera hur tidigare vi använde ERB-motorn för att visa a .erb fil, nu använder vi Builder för att bearbeta en fil. En Builder-fil är oftast en vanlig Ruby-fil med en speciell xml objekt för att skapa XML-taggar.

Starta din visningar / rss.builder visa filen av med följande:

 xml.instruct! : .xml,: version => "1.0" xml.rss: version => "2.0" gör xml.channel slutänden

Mycket viktig anmärkning: På den första sekunden av kodblocket ovan, ta bort perioden (.) i texten : xml. WordPress stör interferens med kod.

Builder kommer att analysera detta för att vara:

     

Så vi har börjat genom att skapa strukturen för en giltig XML-fil. Låt oss nu lägga till taggar för fodertiteln, beskrivningen och en länk tillbaka till huvudsidan. Lägg till följande inuti xml.channel gör blockera:

 xml.title "Recall" xml.description "eftersom du är för upptagen att komma ihåg" xml.link request.url

Lägg märke till hur vi får den aktuella webbadressen från begäran objekt. Vi kan koda detta manuellt, men tanken är att du kan ladda upp appen var som helst utan att behöva byta obskilda kodstycken.

Det finns ett problem men länken är nu inställd på (till exempel) http: // localhost: 9393 / rss.xml. Vi vill helst att länken ska vara till hemsidan, och inte tillbaka till flödet. De begäran objektet har också a PATH_INFO metod som är inställd på den aktuella ruttsträngen; så i vårt fall, /rss.xml.

Att veta detta kan vi nu använda Ruby's chomp metod för att ta bort sökvägen från slutet av webbadressen. Ändra xml.link request.url linje till:

 xml.link request.url.chomp request.path_info

Länken i vår XML-fil är nu inställd på http: // localhost: 9393. Vi kan nu gå igenom varje anteckning och skapa ett nytt XML-objekt för det:

 @ notes.each do | note | xml.item gör 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 slutet slutet

Observera att i raderna 3 och 7 undviker vi notens innehåll med h, precis som vi gjorde i de viktigaste vyerna. Det är lite konstigt att visa samma innehåll för båda titel och den beskrivning taggar, men vi följer Twitters ledning här, och det finns inga andra data vi kan lägga dit.

På rad 6 konverterar vi notens skapad vid tid till RFC822, det önskade formatet för tider i RSS-flöden.

Nu prova det i en webbläsare! Gå till /rss.xml och dina anteckningar ska visas korrekt.


Torka inte upp dig själv

Det finns ett mindre problem med vår implementering. I vår RSS-vy har vi webbplatsens titel och beskrivning. Vi har också fått dem i visningar / layout.erb filen för huvuddelen av webbplatsen. Men nu om vi ville ändra namn eller beskrivning av webbplatsen, finns det två olika platser vi behöver uppdatera. En bättre lösning skulle vara att ange titel och beskrivning i ett placera, hänvisa sedan dem därifrån.

Inuti recall.rb applikationsfil, lägg till följande två rader till toppen av filen direkt efter de fordra uttalanden, för att definiera två konstanter:

 SITE_TITLE = "Recall" SITE_DESCRIPTION = "" för att du är för upptagen att komma ihåg "

Nu tillbaka inuti visningar / rss.builder ändra linjerna 4 och 5 till:

 xml.title SITE_TITLE xml.description SITE_DESCRIPTION

Och inuti visningar / layout.erb ändra </code> tagg på rad 5 till:</p> <pre> <title><%= "#@title | #SITE_TITLE" %>

Och ändra h1 och h2 titelkoder på rad 12 och 13 till:

 

<%= SITE_TITLE %>

<%= SITE_DESCRIPTION %>

Vi bör också inkludera en länk till RSS-flödet i huvud av sidan så att webbläsare kan visa en RSS-knapp i adressfältet. Lägg till följande direkt före märka:

 

Felmeddelanden och framgångar i Flash-meddelanden

Vi behöver något sätt att informera användaren när något gick fel - eller rätt, till exempel ett bekräftelsemeddelande när en ny anteckning läggs till, en anteckning tas bort etc.

Det vanligaste och logiska sättet att uppnå detta är genom "flashmeddelanden" - ett kortmeddelande som läggs till i användarens webbläsarsession, vilket visas och rensas på nästa sida som de ser. Och det råkar bara vara ett par RubyGems för att hjälpa till att uppnå detta! Ange följande i terminalen för att installera Rack Flash och Sinatra Redirect med Flash-pärlor:

 pärla installera rack-flash sinatra-redirect-with-flash

Beroende på hur du har skapat RubyGems på ditt system, kan du behöva prefix gem installation med sudo.

Kräva ädelstenarna och aktivera deras funktionalitet genom att lägga till följande nära toppen av din recall.rb ansökningsfil:

 kräver "rack-flash" kräver "sinatra / redirect_with_flash" aktivera: sessioner använder rack :: Flash,: sweep => true

Att lägga till ett nytt snabbmeddelande är lika enkelt som flash [: error] = "Något gick fel!". Låt oss visa ett fel på hemsidan när det inte finns några anteckningar i databasen.

Ändra din skaffa sig '/' vägen till:

 få '/' do @notes = Note.all: order =>: id.desc @title = 'Alla anteckningar' om @ notes.empty? flash [: error] = 'Inga anteckningar hittades. Lägg till din första nedan. ' end erb: hem slutet

Väldigt enkelt. Om @notes Instansvariabeln är tom, skapa ett nytt blixtfel. För att visa dessa flashmeddelanden på sidan, lägg till följande till din visningar / layout.erb filen, före <%= yield %>:

 <% if flash[:notice] %> 

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

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

Och lägg till följande stilar till din publika / style.css fil för att visa meddelanden i grönt och fel i rött:

 .varsel färg: grön;  .error färg: röd; 

Nu ska din hemsida visa meddelandet "No Notes Found" när databasen är tom:

Låt oss nu visa antingen ett fel eller ett framgångsrikt meddelande beroende på huruvida en ny anteckning kunde läggas till i databasen. Ändra din posta '/' vägen till:

 posta '/' gör n = Note.new n.content = params [: innehåll] n.created_at = Time.now n.updated_at = Time.now om n.save omdirigering '/',: notice => 'Obs .' annars omdirigering '/',: error => 'Misslyckades med att spara anteckning.' änden

Koden är ganska logisk. Om anteckningen skulle kunna sparas, omdirigeras till hemsidan, med ett meddelande om "meddelande", annars omdirigeras hemmet med ett felmeddelande. Här kan du se den alternativa syntaxen för att ställa in ett snabbmeddelande och omdirigera sidan som erbjuds av Sinatra-Redirect-With-Flash-pärlemenan.

Det skulle också vara idealiskt att även visa ett fel på sidan "redigera anteckningen" om den begärda noten inte existerar. Ändra få '/: id' vägen till:

 få '/: id' do @note = Note.get params [: id] @title = "Redigera anteckning ## params [: id]" om @note erb: redigera annan omdirigering '/',: error => "Kan inte hitta den noten." änden

Och även på sidan PUT-förfrågan när du uppdaterar en anteckning. Byta sätta '/: id' till:

 put '/: id' gör n = Note.get params [: id] om inte n omdirigering '/',: error => "Kan inte hitta den noten." slutet n.content = params [: content] n.complete = params [: complete]? 1: 0 n.updated_at = Time.now om n.save omdirigering '/',: notice => 'Not updated successfully.' Annars omdirigering '/',: error => 'Fel vid uppdatering av anteckning.' änden

Ändra få '/: id / delete' vägen till:

 få "/: id / delete" gör @note = Note.get params [: id] @title = "Bekräfta borttagning av anteckning ## params [: id]" om @note erb: redigera annan omdirigering '/' : error => "Kan inte hitta den noten." änden

Och dess motsvarande DELETE-förfrågan, ta bort '/: id' till:

 delete '/: id' gör n = Note.get params [: id] om n.destroy omdirigering '/',: notice => 'Noten raderas framgångsrikt.' annars omdirigering '/',: error => 'Fel vid radering av anteckning.' änden

Ändra ändå få '/: id / complete' rutt till följande:

 få '/: id / complete' gör n = Note.get params [: id] om inte n omdirigering '/',: error => "Kan inte hitta den noten." slutet n.complete = n.complete? 0: 1 # flip it n.updated_at = Time.now om n.save omdirigering '/',: notice => 'Anmärkning markerad som färdig.' annars omdirigering '/',: error => 'Felmarkeringsnotering som fullständig.' änden

Och där har du det!

En fungerande, säker och felaktigt webbapps skrivad i en överraskande liten mängd kod! Under denna korta mini-serie har vi lärt oss hur man behandlar olika HTTP-förfrågningar med ett RESTful-gränssnitt, hantera formulärinsändningar, flykt potentiellt farligt innehåll, ansluta till en databas, arbeta med användarsessioner för att visa blixtmeddelanden, skapa ett dynamiskt RSS-flöde och hur man elegant hanterar applikationsfel.

Om du vill ta appen ytterligare kanske du vill undersöka hanteringen av användarautentisering, till exempel med Sinatra Authentication-gemen.

Om du vill distribuera appen på en webbserver, eftersom Sinatra är byggt med Rake kan du enkelt hosta dina Sinatra-applikationer på Apache och Nginx-servrar genom att installera Passagerare.

Alternativt kan du kolla in Heroku, en Git-powered hostingplattform som gör det enkelt att distribuera dina Ruby web apps git push heroku (gratis konton är tillgängliga!)

Om du vill lära dig mer om Sinatra, kolla in den mycket djupgående Readme, dokumentationssidorna och den kostnadsfria Sinatra-boken.

Notera: källfilerna för varje del av denna mini-serie finns på GitHub, tillsammans med den färdiga appen.