Cântând cu Sinatra - The Encore

Bine ați revenit la cântând cu Sinatra! În această a treia și ultima parte vom extinde aplicația "Recall" pe care am construit-o în lecția anterioară. Vom adăuga un feed RSS la aplicație cu ajutorul unei gem-uri Builder incredibil de util, ceea ce face ca crearea fișierelor XML în Ruby să fie o bucată de prăjitură. Vom învăța cât de ușor va face Sinatra să scape de codul HTML de la utilizatori pentru a preveni atacurile XSS și vom îmbunătăți codul de eroare.


Utilizatorii sunt răi, m'kay

Regula generală atunci când se creează aplicații web trebuie să fie paranoică. Paranoid că fiecare dintre utilizatorii dvs. va ieși să vă aducă prin distrugerea site-ului dvs. sau prin atacarea altor utilizatori prin intermediul acestuia. În aplicația dvs., încercați să adăugați o notă nouă cu următorul conținut:

 Hopa 

În prezent, utilizatorii noștri sunt liberi să introducă orice HTML care le place. Acest lucru lasă aplicația deschise spre atacurile XSS unde un utilizator poate intra în JavaScript greșit pentru a ataca sau pentru a îndrepta alți utilizatori ai site-ului. Deci, primul lucru pe care trebuie să-l facem este să eliminăm conținutul trimis de utilizatori, astfel încât codul de mai sus să fie convertit în entități HTML, cum ar fi:

 Hopa 

Pentru a face acest lucru, adăugați următorul bloc de cod la dvs. recall.rb fișier, de exemplu, în cadrul DataMapper.auto_upgrade! linia:

 ajutătorii includ Rack :: Utils alias_method: h,: escape_html sfârșit

Acesta include un set de metode furnizate de Rack. Acum avem acces la a h () metoda de a scapa HTML.

Pentru a scăpa de HTML în pagina de pornire, deschideți vizualizari / home.erb vizualizați fișierul și schimbați <%= note.content %> line (în jurul liniei 11) la:

 <%=h note.content %>

Alternativ, am fi putut scrie acest lucru <%= h(note.content) %>, dar stilul de mai sus este mult mai comun în comunitatea Ruby. Reîmprospătați pagina și codul HTML trimis ar trebui să fie acum scos și nu executat de browser:

XSS pe celelalte pagini

Faceți clic pe linkul "editați" pentru nota cu codul XSS și vă puteți gândi că este în siguranță - totul stă în interiorul unei textarea și astfel nu se execută. Dar dacă adăugăm o notă nouă cu următorul conținut:

  

Uitați-vă la pagina de editare a acestuia și puteți vedea că am închis textarea și astfel alerta JavaScript este executată. Este clar că trebuie să scăpăm de conținutul notei pe fiecare pagină în care este afișată.

În interiorul tău vizualizari / edit.erb vizualizați fișierul, evitați conținutul din interiorul lui textarea prin rularea prin h (linia 4):

 

Și faceți același lucru în dvs. vizualizari / delete.erb fișierul de pe linia 2:

 

Sigur doriți să ștergeți următoarea notă: "<%=h @note.content %>"?

Acolo îl aveți - suntem în siguranță de la XSS. Nu uitați să scăpați de toate datele trimise de utilizatori atunci când creați alte aplicații web pe viitor!

S-ar putea să vă întrebați "ce este vorba de injecțiile SQL?" Ei bine, DataMapper se ocupă de asta pentru noi, atâta timp cât folosim metodele DataMapper pentru a obține date din baza de date (adică nu execută SQL brut).


RSS Feed Masses

O parte importantă a oricărui site dinamic este o formă de feed RSS, iar aplicația Recall nu va fi o excepție! Din fericire incredibil ușor de creat feed-uri grație bijuterie Builder. Instalați-l cu:

 gem install builder

În funcție de modul în care ați instalat RubyGems pe sistemul dvs., este posibil să aveți nevoie să prefixați gem install cu sudo.

Acum adăugați o nouă rută spre dvs. recall.rb fișierul de cerere pentru o solicitare GET către /rss.xml:

 primiți '/rss.xml' la @notes = Note.all: order =>: id.desc builder: rss end

Asigurați-vă că adăugați acest traseu undeva de mai sus obține '/: id' ruta, altfel o cerere pentru rss.xml ar fi confundat cu un ID post!

În traseu, pur și simplu solicităm toate notele din baza de date și încărcăm a rss.builder Vezi fișierul. Rețineți cum am folosit motorul ERB pentru a afișa a .ERB fișier, acum folosim Builder pentru a procesa un fișier. Un fișier Builder este în mare parte un fișier normal Ruby cu o caracteristică specială xml obiect pentru crearea de etichete XML.

Începeți-vă vizualizari / rss.builder vizualizați fișierul cu următoarele:

 xml.instruct! : .xml,: version => "1.0" xml.rss: version => "2.0" do xml.channel nu se termina

Notă foarte importantă: În prima secundă a blocului de cod de mai sus, eliminați perioada (.) in text : .xml. WordPress interferă cu fragmente de cod.

Builder va analiza acest lucru pentru a fi:

     

Așa că am început prin crearea unei structuri pentru un fișier XML valid. Acum, să adăugăm etichete pentru titlul feed-ului, descrierea și un link înapoi la site-ul principal. Adăugați următoarele în interiorul xml.channel do bloc:

 xml.title "Recall" xml.description "deoarece sunteți prea ocupat să vă amintiți" xml.link request.url

Observați cum obținem adresa URL curentă din cerere obiect. Am putea codifica acest lucru manual, însă ideea este că puteți încărca aplicația oriunde fără a fi nevoie să modificați coduri obscure.

Există totuși o problemă, legătura este setată acum (de exemplu) http: // localhost: 9393 / rss.xml. În mod ideal, dorim ca linkul să fie la pagina de pornire și nu înapoi la feed. cerere obiect are, de asemenea, o PATH_INFO care este setată la șirul de ruta curent; așa că în cazul nostru, /rss.xml.

Știind acest lucru, putem folosi acum Ruby's Chomp pentru a elimina calea de la sfârșitul adresei URL. Schimba xml.link request.url line la:

 request request.url.chomp cererii xml.link.path_info

Legătura din fișierul nostru XML este acum setată la http: // localhost: 9393. Putem acum trece prin fiecare notă și să creăm un nou element XML pentru aceasta:

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

Rețineți că pe liniile 3 și 7 evităm utilizarea conținutului notei h, la fel cum am făcut și în opiniile principale. Este puțin ciudat să afișați același conținut pentru ambele titlu si Descriere tag-uri, dar urmărim conducerea Twitter aici și nu există alte date pe care le putem pune acolo.

Pe linia 6 convertim nota creat la timpul pentru RFC822, formatul necesar pentru orele din fluxurile RSS.

Acum, încercați-l într-un browser! Mergi la /rss.xml iar notele dvs. ar trebui să fie afișate corect.


DRY Nu vă repetați

Există o mică problemă cu implementarea noastră. În vizualizarea noastră RSS avem titlul și descrierea site-ului. De asemenea, le-am luat în vizualizari / layout.erb fișier pentru partea principală a site-ului. Dar acum dacă vrem să schimbăm numele sau descrierea site-ului, există două locuri diferite pe care trebuie să le actualizăm. O soluție mai bună ar fi să setați titlul și descrierea în unu locul, apoi să le menționați de acolo.

În interiorul recall.rb , adăugați direct următoarele două linii în partea de sus a fișierului după necesita declarații, pentru a defini două constante:

 SITE_TITLE = "Recuperare" SITE_DESCRIPTION = "'pentru că sunteți prea ocupat să vă amintiți"

Acum, înapoi înăuntru vizualizari / rss.builder modificați liniile 4 și 5 la:

 xml.title SITE_TITLE xml.description SITE_DESCRIPTION

Și înăuntru vizualizari / layout.erb schimba </code> indicați pe linia 5:</p> <pre> <title><%= "#@title | #SITE_TITLE" %>

Și schimbați h1 și h2 titlurile de titlu de pe liniile 12 și 13 la:

 

<%= SITE_TITLE %>

<%= SITE_DESCRIPTION %>

De asemenea, ar trebui să includeți un link la feedul RSS din cap a paginii, astfel încât browserele să poată afișa un buton RSS în bara de adrese. Adăugați următoarele în mod direct înainte de etichetă:

 

Erori și succese ale mesajelor erotice

Avem nevoie de o modalitate de a informa utilizatorul când sa întâmplat ceva - sau drept, cum ar fi un mesaj de confirmare când se adaugă o notă nouă, o notă eliminată etc..

Cea mai obișnuită și mai logică modalitate de a realiza acest lucru este prin "mesajele flash" - un mesaj scurt adăugat în sesiunea de browser a utilizatorului, care este afișat și șters pe pagina următoare pe care o vizualizează. Și așa se întâmplă să fie un cuplu de RubyGems pentru a ajuta la realizarea acestui lucru! Introduceți următoarele în Terminal pentru a instala Rack Flash și Sinatra Redirect cu pietre Flash:

 gem instalați rack-flash sinatra-redirect-cu-bliț

În funcție de modul în care ați instalat RubyGems pe sistemul dvs., este posibil să aveți nevoie să prefixați gem install cu sudo.

Solicitați pietrele prețioase și activați funcționalitatea acestora, adăugând următoarele în partea de sus a paginii dvs. recall.rb fișier de aplicație:

 necesită 'rack-flash' necesită 'sinatra / redirect_with_flash' permite: sesiunile utilizează Rack :: Flash,: sweep => true

Adăugarea unui nou mesaj flash este la fel de simplă flash [: error] = "Ceva a mers prost!". Să afișăm o eroare în pagina de pornire când în baza de date nu există note.

Schimba-ti obține '/' ruta spre:

 get '/' do @notes = Note.all: order =>: id.desc @title = 'Toate notele' dacă @ notes.empty? flash [: error] = 'Nu s-au găsit note. Adăugați primul dvs. mai jos. " end erb: sfârșitul casei

Foarte simplu. În cazul în care @notes instanta este goala, creeaza o noua eroare flash. Pentru a afișa aceste mesaje flash pe pagină, adăugați următoarele la dumneavoastră vizualizari / layout.erb fișier, înainte de <%= yield %>:

 <% if flash[:notice] %> 

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

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

Și adăugați următoarele stiluri la dvs. publice / style.css fișier pentru afișarea anunțurilor în verde și erori în roșu:

 .notă culoare: verde;  .error culoare: roșu; 

Acum, pagina dvs. de pornire ar trebui să afișeze mesajul "nu există note" când baza de date este goală:

Acum, să afișăm o eroare sau un mesaj de succes în funcție de faptul dacă o notă nouă ar putea fi adăugată la baza de date. Schimba-ti post '/' ruta spre:

 post '/' do n = Notă.new n.content = params [: content] n.created_at = Time.now n.updated_at = Time.now dacă n.save redirect '/',: note => 'Notă creată cu succes .“ else redirect '/',: error => 'Nota nu a fost salvata'. sfârșitul final

Codul este destul de logic. Dacă nota poate fi salvată, redirecționați-vă către pagina de pornire, cu un mesaj flash "notificare", altfel redirecționați-vă acasă cu un mesaj flash de eroare. Aici puteți vedea sintaxa alternativă pentru setarea unui mesaj flash și redirecționarea paginii oferite de bijuteria Sinatra-Redirect-With-Flash.

De asemenea, ar fi ideal să afișați și o eroare pe pagina "editați nota" dacă nota solicitată nu există. Schimba obține '/: id' ruta spre:

 '': 'id: id =' id = 'id =' id = 'id = "Nu găsesc acea notă." sfârșitul final

Și, de asemenea, pe pagina de solicitare PUT pentru actualizarea unei note. Schimbare pune '/: id' la:

 pune '/: id' do n = Note.get params [: id] cu excepția cazului n redirect '/',: error => "Nu găsesc acea notă". end n.content = params [: conținut] n.complete = params [: complete]? 1: 0 n.updated_at = Time.now dacă n.save redirect '/',: note => 'Notă actualizată cu succes.' alt redirect '/',: error => 'Eroare la actualizarea notei.' sfârșitul final

Schimba obțineți / /: id / delete ' ruta spre:

 "@: eroare: editează altul redirecționează" / ',' '': id / delete ' : error => "Nu găsesc acea notă." sfârșitul final

Și cererea corespunzătoare DELETE, ștergeți '/: id' la:

 ștergeți '/: id' do n = Note.get params [: id] dacă n.destroy redirect '/',: note => 'Notă ștersă cu succes'. altceva redirect '/',: error => 'Eroare la ștergerea notei'. sfârșitul final

În cele din urmă, schimbați obține '/: id / complete' urmăriți următoarele:

 primi '/: id / complete' do n = Note.get params [: id] cu excepția cazului n redirect '/',: error => "Nu găsesc acea notă". end n.complete = n.complete? 0: 1 # flip it n.updated_at = Time.now dacă n.save redirect '/',: note => 'Notă marcată ca completă'. altceva redirect '/',: error => 'Eroare la marcarea notei ca fiind completă'. sfârșitul final

Și acolo aveți!

O aplicație Web funcțională, sigură și responsabilă de erori, scrisă într-o sumă surprinzător de mică de cod! Pe această mini-serie scurtă am învățat cum să procesăm diferite cereri HTTP cu o interfață RESTful, să ne ocupăm de trimiterile de formulare, să scăpăm de conținut potențial periculos, să conectăm o bază de date, să lucrăm cu sesiunile utilizatorilor pentru a afișa mesaje flash, cum să se ocupe de erorile aplicației.

Dacă doriți să luați aplicația mai departe, vă recomandăm să vă ocupați de autentificarea utilizatorului, cum ar fi cu bijuteria de autentificare Sinatra.

Dacă doriți să implementați aplicația pe un server web, deoarece Sinatra este construit cu Rake, puteți foarte ușor să găzduiți aplicațiile Sinatra pe serverele Apache și Nginx instalând Passenger.

Alternativ, verificați-l pe Heroku, o platformă de găzduire bazată pe Git, care face ca implementarea aplicațiilor web Ruby să fie la fel de simplă git push heroku (conturile gratuite sunt disponibile!)

Dacă doriți să aflați mai multe despre Sinatra, consultați documentele Readme în profunzime, Paginile de documentare și Cartea Sinatra gratuită.

Notă: fișierele sursă pentru fiecare parte a acestei mini-serii sunt disponibile pe GitHub, împreună cu aplicația finită.

Cod