Skocz do zawartości

Witaj na forum webmasterów Webax.pl.

Wyświetl nową zawartość

Ruby on Rails - alternatywa PHP - część szósta - walidacja


  • Zaloguj się, aby dodać odpowiedź
9 odpowiedzi w tym temacie
Soanvig
Soanvig

    Początkujący

  • Użytkownik
  • PipPipPip
  • 114 postów
#1

Napisano 06 wrzesień 2012 - 21:53

Witam po długiej przerwie. Byłem trochę zdemotywowany tym, że nie było odzewu najmniejszego na mój ostatni poradnik - dlatego nie pisałem dalszych części. Mimo wszystko pisałem sobie w Rails i napisałem upload plików + logowanie + lista plików. I znowu mnie wzięła ochota na RoR, więc postanowiłem napisać kolejną część.

Dzisiaj przerobimy walidację danych z formularza rejestracji i wyświetlanie błędów. Pisanie przeniosłem na Windowsa, ponieważ wkurzało mnie nie do końca poprawne działanie maszyny wirtualnej z Mintem, a na Win też się pisze jak najbardziej w porządku.

Walidację możemy zrobić na dwa sposoby - sprawdzić dane ręcznie w kontrolerze, lub walidować dane, które poprzez model mają trafić do tabeli. Zajmiemy się drugim sposobem, gdyż więcej się przy nim można nauczyć. Wykorzystamy wcześniej poznaną funkcję .save i fakt, że zwraca ona true lub false.
Wróćmy więc do naszego kontrolera account i do metody new:
def new	redirect_to :action => "start" if session[:user_id]	if params["user"]		data = {					"username" => params["user"]["username"],					"password" => params["user"]["password"],					"email" => params["user"]["email"],					"register_date" => DateTime.now.to_date					}		@save = User.new(data)		@save.save	endend
Po wywołaniu @save.save zostanie wykonany zapis do bazy danych. Zapis ten będzie przebiegał za pośrednictwem modelu User. Przejdźmy więc do niego: ./app/models/user.rb
class User < ActiveRecord::Baseattr_accessible :email, :password, :register_date, :usernameend
attr_accessible - jak sama nazwa wskazuje - umożliwia dostęp do wymienionych zmiennych i edytowanie ich z zewnątrz. Jest to konieczne jeśli chcemy do tych danych odnieść się np. poprzez metodę .save umieszczoną w kontrolerze, a nie bezpośrednio w modelu, w którym dane zmienne istnieją.

Sprawdzanie danych, które przechodzą przez model, można wykonać poprzez funkcję istniejącą tylko dla modelów. Mówię tu o validates. Spróbujmy ją przeanalizować na podstawie kilku przykładów, które wykorzystamy w naszej aplikacji:
validates :username, :presence => true, :length => { :minimum => 4, :maximum => 15 }
Konstrukcja, jak większość rzeczy w Railsach jest prosta, logiczna. Naprawdę - prościej się nie da Posted Image W Rubim nie potrzeba podawać argumentów w nawiasach - wystarczą spacje i przecinki.
Pierwszym parametrem jest zmienna, którą chcemy walidować. Kolejne to są rzeczy, które chcemy sprawdzać. W tym wypadku mamy: obecność zmiennej (czyli generalnie to, że nie jest pusta/jest w ogóle przesłana), minimalna długość stringu w znakach, oraz maksymalna długość stringu w znakach. Można podać np. tylko minimalną, tylko maksymalną, tylko obecność itd. Wydawałoby się logiczne, że skoro jest podana minimalna długość znaków, to po co sprawdzać obecność zmiennej (jeśli jest spełniony warunek minimalnej długości znaków - zmienna musi istnieć). Lepiej to jednak zrobić, niż mieć potem problemy z użyszkodnikami. Nie zmniejsza to znacząco wydajności skryptu, a dodatkowe zabezpieczenia przed zaawansowanymi technikami włamywania się nie zaszkodzą (nie mam pojęcia, czy takie coś istnieje, ale w kursie, z którego ja się uczyłem, przykład sprawdzał i obecność zmiennej i minimalną długość).

Spróbujmy napisać warunki innych danych, wprowadzanych przez użytkownika - pozostały nam: email oraz hasło. Sprawdźmy je za pomocą wyrażeń regularnych:
validates :email, :format => { :with => /^[a-zA-Z0-9.-_]+@[a-zA-Z0-9.-_]+.[a-z]{2,4}$/ }
Teraz sprawdźmy hasło i jego minimalną długość (minimalna długość jest równa obecności hasła w ogóle)
validates :password, :length => { :minimum => 4 }
Zgodność z wyrażeniem regularnym sprawdzamy email za pomocą :format oraz parametru :with. Wyrażenie regularne podajemy w formie bezpośredniej, nie zamykając go w cudzysłowie, a jedynie w standardowych slashach.
Walidację parametrów hasła pomijamy, gdyż aby hasło było w bazie bezpieczne należy przeprowadzić jego hashowanie. A haszowane hasło niezależne od znaków jakie zawiera, ma zawsze taką samą długość oraz tą samą pulę znaków - generalnie sprawdzanie poprawności hasha byłoby kompletnie bezcelowe. Tak więc wróćmy do kontrolera account.

Jak już wspominałem setki razy, save zwróci true, jeśli model przyjął dane, a false jeśli odrzuci. Kiedy jakaś część nie przejdzie walidacji, model zwróci false. Tak więc sprawdźmy czy model przyjął nasze dane, modyfikując @save.save:
...if @save.save	# Tutaj trafi kod logowania i przekierowanieend# jeśli warunek nie jest spełniony, nie zostanie wykonane przekierowanie i wyrenderuje się widok metody new
Okej, znowu ukaże nam się formularz. Ale użytkownika trzeba poinformować o tym, że wprowadzone przez niego dane nie przeszły walidacji. Przejdźmy więc do widoku new i dodajmy na początku pliku sprawdzanie czy wystąpiły jakieś błędy przy zmiennej instancji (zmiennej danej metody) @save:
<% if @save and @save.errors %><ul>	<% @save.errors.full_messages.each do |err| %>	 <li><%= err %></li><% end %></ul><% end %>
Kod wydaje się być logiczny. Po kolei - najpierw leci warunek, sprawdzający czy w ogóle zmienna została zdefiniowana (patrząc na nasz kod w kontrolerze - jeśli user nie prześle parametrów, zmienna nie jest zdefiniowana, a wtedy sprawdzanie metody .errors na nieistniejącej metodzie zwróci błąd). Jako drugie sprawdzamy, czy w istniejącej już zmiennej zostały zmieszczone jakieś błędy. Jeśli tak, ma wyświetlić listę, w której poszczególne podpunkty stanowią błędy napisane w języku railsowym (dostosowanie tych komunikatów [tak, aby np. były po polsku] w zalecany sposób sprowadza się do użycia modułu tłumaczeń, a o tym już proszę doczytać w sieci, gdyż naprawdę za dużo pisania Posted Image). Błędy te wyświetla się za pomocą pętli operującej na tablicy, w której dane błędy są zawarte. I ten kod cał powyżej należy wstawić w miejscu, w którym chcemy mieć wyświetlone błędy.

Wróćmy znowu do kontrolera, aby przeprowadzić proces haszowania hasła.
Najpierw należy dołączyć bibliotekę dostarczającą klasy do haszowania - od razu po sprawdzeniu, czy przesłano jakieś parametry trzeba dopisać linijkę: require 'digest/sha2', a następnie hasło wrzucić do klasy Digest::SHA2.new. Końcowy kod wygląda następująco:
if params["user"]	require 'digest/sha2'	password = Digest::SHA2.new << params["user"]["password"]	data = {				"username" => params["user"]["username"],				"password" => password.to_s,				"email" => params["user"]["email"],				"register_date" => DateTime.now.to_date				}	@save = User.new(data)	if @save.save		# Tutaj trafi kod logowania i przekierowanie	endend
Dobrze, walidację mamy skończoną. Teraz spróbujmy zarejestrować się, przesyłając dane niezgodne z walidacją - np. przesyłając puste pole nick, lub wpisując adres e-mail bez znaku @.

U mnie wszystko działa, u was też powinno Posted Image W następnej części omówimy proces zalogowania i wylogowania.

EDIT:
Dodana walidacja hasła
Naprawiony błąd z przekierowaniem (w metodzie new na początku kodu powinno przekierowywać do metody start, jeśli użytkownik jest zalogowany)
UWAGA: został poprawiony błąd przy zapisywaniu danych do bazy (przy password pojawiła się konwersja na string: .to_s)
  • 1

[font="Courier;"].oooooooo8....ooooooo......o......oooo...oooo.888.........o888...888o...888......8888o..88...888oooooo..888.....888..8..88.....88.888o88..........888.888o...o888.8oooo88....88...8888..o88oooo888....88ooo88.o88o..o888o.o88o....88..[/font]


Thelleo
Thelleo

    Początkujący

  • Użytkownik
  • PipPipPip
  • 99 postów
#2

Napisano 06 wrzesień 2012 - 22:18

Ciekawe, ciekawe ;) W Rubym się szybko pisze... za szybko... taki programista PHP jak ja nie wyrabia :D
  • 0

Dołączona grafika


Soanvig
Soanvig

    Początkujący

  • Użytkownik
  • PipPipPip
  • 114 postów
#3

Napisano 07 wrzesień 2012 - 15:24

coś w tym jest
  • 0

[font="Courier;"].oooooooo8....ooooooo......o......oooo...oooo.888.........o888...888o...888......8888o..88...888oooooo..888.....888..8..88.....88.888o88..........888.888o...o888.8oooo88....88...8888..o88oooo888....88ooo88.o88o..o888o.o88o....88..[/font]


Comandeer
Comandeer

    Stały bywalec

  • Developer
  • 418 postów
#4

Napisano 07 wrzesień 2012 - 18:33

jest w Ruby bcrypt?
  • 0

Moje tutoriale

Ad hominem attacks are valid arguments, and you are an idiot.


Soanvig
Soanvig

    Początkujący

  • Użytkownik
  • PipPipPip
  • 114 postów
#5

Napisano 07 wrzesień 2012 - 18:42

W cholerę dużo pluginów do rubiego jest, więc:http://bcrypt-ruby.rubyforge.org/
  • 0

[font="Courier;"].oooooooo8....ooooooo......o......oooo...oooo.888.........o888...888o...888......8888o..88...888oooooo..888.....888..8..88.....88.888o88..........888.888o...o888.8oooo88....88...8888..o88oooo888....88ooo88.o88o..o888o.o88o....88..[/font]


cap'n
cap'n

    Początkujący

  • Aktywny
  • 67 postów
#6

Napisano 11 listopad 2012 - 17:29

A haszowane hasło niezależne od znaków jakie zawiera, ma zawsze taką samą długość oraz tą samą pulę znaków - generalnie sprawdzanie poprawności hasha byłoby kompletnie bezcelowe.

Poprawności hasha nie, ale poprawności hasła już tak! I, co więcej, powinno się to robić. Używając Twojego przykładu (który, nawiasem mówiąc, nie jest do końca optymalny) należałoby przerzucić hashowanie hasła do modelu (takie rzeczy zawsze należy przerzucać do modelu zgodnie z MVC), poprzez np. metodę
def hash_password
require 'digest/sha2'
self.password = Digest::SHA2.new << password
end

... i podpiąć ją przez callback ...
after_validation :hash_password

I wtedy możesz spokojnie walidować hasło, żeby miało odpowiednią długość czy różnorodność znaków.

Ale ogólnie praise the Lord, że ktoś z backgroundu PHP-owego zwraca uwagę na walidację jako coś dobrego ;)
  • 1

Soanvig
Soanvig

    Początkujący

  • Użytkownik
  • PipPipPip
  • 114 postów
#7

Napisano 11 listopad 2012 - 23:07

Możemy walidować hasło, jeśli chcemy, żeby miało konkretne parametry, ale ja osobiście nie lubię ograniczać użytkownika :) Ale powinienem zrobić minimalną ilość znaków ^^Dzięki za info o after_validation
  • 0

[font="Courier;"].oooooooo8....ooooooo......o......oooo...oooo.888.........o888...888o...888......8888o..88...888oooooo..888.....888..8..88.....88.888o88..........888.888o...o888.8oooo88....88...8888..o88oooo888....88ooo88.o88o..o888o.o88o....88..[/font]


Thelleo
Thelleo

    Początkujący

  • Użytkownik
  • PipPipPip
  • 99 postów
#8

Napisano 17 listopad 2012 - 12:52

Do validates w modelu (dla :username i :email) warto też dodać
:uniqueness => true
żeby nikt nie zarejestrował się dwa razy na te same dane. Całość u mnie wygląda tak:
class User < ActiveRecord::Base  attr_accessible :email, :password, :register_date, :username  validates :username, :uniqueness => true, :presence => true, :length => { :minimum => 3, :maximum => 15 }  validates :email, :uniqueness => true, :format => { :with => /^[a-zA-Z0-9.-_]+@[a-zA-Z0-9.-_]+.[a-z]{2,4}$/ }end

  • 1

Dołączona grafika


Mattground
Mattground

    Użytkownik

  • Użytkownik
  • PipPip
  • 15 postów
#9

Napisano 26 grudzień 2012 - 03:38

Planujesz napisać kolejną część tutorialu?
  • 0

Soanvig
Soanvig

    Początkujący

  • Użytkownik
  • PipPipPip
  • 114 postów
#10

Napisano 26 grudzień 2012 - 12:06

Niech wam będzie, jak wymyślę co dalej to napiszę. Ale już oficjalnie przysięgam, że napiszę coś dalej :)W międzyczasie dodałem walidację hasła.
  • 0

[font="Courier;"].oooooooo8....ooooooo......o......oooo...oooo.888.........o888...888o...888......8888o..88...888oooooo..888.....888..8..88.....88.888o88..........888.888o...o888.8oooo88....88...8888..o88oooo888....88ooo88.o88o..o888o.o88o....88..[/font]