HTTP klient pomocí Ruby Net:HTTP a Sinatra

8. 7. 2016
Doba čtení: 10 minut

Sdílet

Ukážeme si, ako vytvoriť HTTP klientov pomocou modulu Ruby Net::HTTP. Vytvoríme skripty, ktoré budú sťahovať a posielať dáta, pracovať s JSON a napoja sa na zabezpečenú stránku.

Net:HTTP

HTTP je internetový protokol určený pre výmenu hypertextových dokumentov vo formáte HTML. Net:HTTP je štandardný Ruby modul pre vytváranie HTTP klientov pomocou ktorých posielame HTTP požiadavky a prijímame HTTP odpovede. Ruby má vo svojom obsiahlom balíčkovom systéme viaceré ďalšie knižnice pre prácu s HTTP, napríklad Faraday, HTTPClient, alebo rest-client.

Sinatra

Sinatra je populárny webový aplikačný framework určený pre jazyk Ruby. Jeho popularita pramení hlavne z jednoduchosti, s akou si môžeme vytvárať menšie webové aplikácie. Sinatra je alternatívou k Ruby on Rails, ktorý je v súčasnosti najpopulárnejší Ruby framework; má však oveľa strmšiu krivku učenia. Sinatra má domovskú stránku na www.sinatrarb.com.

Niektorí z našich klientov sa budú napájať na Sinatra aplikácie.

$ sudo gem install sinatra
$ sudo gem install thin

Pomocou týchto príkazov si nainštalujeme Sinatru a webový server Thin. Ak je Thin nainštalovaný, Sinatra si ho automaticky zvolí namiesto zabudovaného serveru WEBrick.

Vytvorme si projektový adresár a v ňom nasledujúci main.rb  súbor:

require 'sinatra'

get '/' do
    "First application"
end

V tejto jednoduchej Sinatra aplikácii reagujeme na prístup k ceste /. Aplikácia vracia krátku správu klientovi.

$ ruby main.rb
== Sinatra (v1.4.7) has taken the stage on 4567 for development with backup from Thin
Thin web server (v1.6.4 codename Gob Bluth)
Maximum connections set to 1024
Listening on localhost:4567, CTRL+C to stop

Aplikáciu štartujeme príkazom ruby main.rb. Spustí sa Thin server, ktorý načúva na porte 4567.

$ curl localhost:4567/
First application

Pomocou utility curl sa pripojíme na server a na cestu /. Na termináli sa nám zobrazí správa.

V príkladoch, kde budeme mať Sinatra aplikáciu, si vždy najprv spustíme Sinatru pomocou $ ruby main.rb v jednom termináli a v inom spustíme Ruby HTTP klienta, ktorý sa bude pripájať a aplikácii; napríklad: $ ./get_content.rb.

Stiahnutie obsahu stránky

Metóda get_print je vysokoúrovňovou metódou na stiahnutie obsahu dokumentu. Metóda zároveň zobrazí stiahnutý obsah na termináli.

#!/usr/bin/ruby

require 'net/http'

uri = URI 'http://www.something.com/'

Net::HTTP.get_print uri

Skript stiahne obsah stránky www.something.com. Modul net/http úzko spolupracuje s modulom uri.

require 'net/http'

Tento príkaz nám dáva prístup k obom modulom: net/http a uri; takže nemusíme použiť príkaz zvlášť aj pre uri.

$ ./get_content.rb
<html><head><title>Something.</title></head>
<body>Something.</body>
</html>

Toto je výstup programu get_content.rb.

Nasledujúci program stiahne webový dokument a odstráni z neho HTML značky.

#!/usr/bin/ruby

require 'net/http'

uri = URI "http://www.something.com/"

doc = Net::HTTP.get uri

puts doc.gsub %r{</?[^>]+?>}, ''

Tento skript využíva metódu get na stiahnutie obsahu stránky www.something.com a odstráni z neho HTML značky.

puts doc.gsub %r{</?[^>]+?>}, ''

Na odstránenie HTML značiek využijeme regulárny výraz.

$ ./strip_tags.rb
Something.
Something.

Skript zobrazí titulok a obsah stránky.

Status

Metódy code a message nám udávajú status HTTP odpovede.

#!/usr/bin/ruby

require 'net/http'

uri = URI 'http://www.something.com'
res = Net::HTTP.get_response uri
puts res.message
puts res.code

uri = URI 'http://www.something.com/news/'
res = Net::HTTP.get_response uri
puts res.message
puts res.code

uri = URI 'http://www.urbandicionary.com/define.php?term=Dog'
res = Net::HTTP.get_response uri
puts res.message
puts res.code

V tomto programe pošleme tri rôzne HTTP požiadavky pomocou metódy get_response a zistíme status HTTP odpovedí.

uri = URI 'http://www.something.com/news/'
res = Net::HTTP.get_response uri
puts res.message
puts res.code

Status HTTP odpovede získame pomocou metód message a code. Keďže daný zdroj nie je platný, dostaneme ako odpoveď kód 404, Not Found.

$ ./status.rb
OK
200
Not Found
404
Found
302

200 je kód pre úspešnú HTTP požiadavku, 404 nám hovorí, že daný zdroj nebolo možné nájsť a 302 znamená, že zdroj bol dočasne premiestnený.

Metóda head

Metóda head slúži na získanie hlavičky dokumentu. Hlavička obsahuje rôzne polia opisujúce dokument, napríklad názov obsluhujúceho servera, typ a veľkosť dokumentu, alebo čas poslednej modifikácie dokumentu. Týmto informáciám sa hovorí metadáta.

#!/usr/bin/ruby

require 'net/http'

uri = URI "http://www.something.com"
http = Net::HTTP.new uri.host, uri.port

res = http.head '/'

puts res['server']
puts res['date']
puts res['last-modified']
puts res['content-type']
puts res['content-length']

Program stiahne HTML dokument z adresy www.something.com a vypíše názov servera, dátum HTTP odpovede, čas poslednej modifikácie dokumentu, typ dokumentu a jeho veľkosť.

$ ./head.rb
Apache/2.4.12 (FreeBSD) OpenSSL/1.0.1l-freebsd mod_fastcgi/mod_fastcgi-SNAP-0910052141
Wed, 11 May 2016 19:30:56 GMT
Mon, 25 Oct 1999 15:36:02 GMT
text/html
77

Z výstupu vidíme, že webové stránky manažuje server Apache na operačnom systéme FreeBSD. Dokument bol naposledy modifikovaný v roku 1999. Stiahnutým obsahom je HTML dokument, ktorý má veľkosť 77 bajtov.

Metóda get

Metóda get vykoná požiadavku GET smerom k serveru. GET žiada server o špecifický zdroj.

require 'sinatra'

get '/greet' do
    "Hello #{params[:name]}"
end

Toto je súbor aplikácie Sinatra. Keď zachytíme prístup k ceste /greet, vrátime správu, ktorá bude obsahovať meno zaslané klientom.

#!/usr/bin/ruby

require 'net/http'

uri = URI "http://localhost:4567/greet"

params = { :name => 'Peter' }
uri.query = URI.encode_www_form params

puts Net::HTTP.get uri

Tento skript pošle premennú s hodnotou našej Sinatra aplikácii. Premenná je zakódovaná do URL identifikátora.

params = { :name => 'Peter' }

Toto je parameter, ktorý zašleme serveru.

uri.query = URI.encode_www_form params

Parameter zakódujeme do identifikátora zdroja pomocou metódy encode_www_form method.

puts Net::HTTP.get uri

Pomocou metódy get pošleme GET požiadavku serveru. Server nám vráti odpoveď, ktorú vypíšeme na terminál.

$ ./mget.rb
Hello Peter

Toto je výstup programu.

127.0.0.1 - - [11/May/2016:21:51:12 +0200] "GET /greet?name=Peter HTTP/1.1" 200 11 0.0280

V tomto logu servera Thin môžeme vidieť, že parameter bol zakódovaný do URL identifikátora.

User agent

User-Agent je pole hlavičky HTTP požiadavky, ktorou sa identifikuje klient. V ďalšom príklade si ukážeme, ako si nastaviť názov vlastného agenta v požiadavke, ktorú pošleme Sinatra aplikácii.

require 'sinatra'

get '/agent' do
    request.user_agent
end

Táto Sinatra aplikácia zistí názov klienta, ktorý vykonal HTTP požiadavku a pošle ho späť klientovi.

#!/usr/bin/ruby

require 'net/http'

uri = URI "http://localhost:4567"
http = Net::HTTP.new uri.host, uri.port

res = http.get '/agent', {'User-Agent' => 'Ruby script'}
puts res.body

Tento skript pošle GET požiadavku, v hlavičke ktorej sa nachádza nami nastavené pole User-Agent.

res = http.get '/agent', {'User-Agent' => 'Ruby script'}

Voľbu User-Agent sme pridali ako druhý parameter metóde get.

$ ./agent.rb
Ruby script

Server odpovedal názvom nášho klienta, ktorý sme mu zaslali v požiadavke.

Metóda post

Metódou post zašleme požiadavku POST na uvedený URL zdroj. Zasielané dáta nie sú pridané do URL.

require 'sinatra'

post '/target' do
    "Hello #{params[:name]}"
end

Táto Sinatra aplikácia vracia správu v prípade prístupu na cestu /target. Spracuje dáta poslané POST metódou a zahrnie ich do správy klientovi.

#!/usr/bin/ruby

require 'net/http'

uri = URI "http://localhost:4567/target"

params = { :name => 'Peter' }
res = Net::HTTP.post_form uri, params

puts res.body

Tento skript vygeneruje POST požiadavku s premennou name, ktorá má hodnotu Peter. POST požiadavku zabezpečí metóda Net::HTTP.post_form.

$ ./mpost.rb
Hello Peter

Toto je výstup skriptu mpost.rb.

127.0.0.1 - - [12/May/2016:11:36:16 +0200] "POST /target HTTP/1.1" 200 11 0.0006

Z logu web servera vidíme, že požiadavka POST nepridáva posielané dáta do URL zdroja.

Stiahnutie definícií z online slovníka

V ďalšom príklade si vyhľadáme definíciu slova zo stránky www.dictionary.com. Na parsovanie HTML kódu použijeme modul nokogiri. Nokogiri je najobľúbenejší Ruby HTML, XML, SAX a Reader parser. Modul sa nainštaluje príkazom sudo gem install nokogiri.

#!/usr/bin/ruby

require 'net/http'
require 'nokogiri'

term = 'cat'
uri = URI 'http://www.dictionary.com/browse/'+term

res = Net::HTTP.get uri

doc = Nokogiri::HTML res
doc.css("div.def-content").map do |node|
    s = node.text.strip!
    s.gsub!(/\s{3,}/, " ") unless (s == nil)
    puts s unless (s == nil)
end

Pomocou tohto skriptu nájdeme definície slova ‚cat‘ zo stránky www.dictionary.com.

uri = URI 'http://www.dictionary.com/browse/'+term

Zvolený termín pridáme na koniec URL zdroja.

res = Net::HTTP.get uri

Pomocou get metódy získame odpoveď servera, v ktorej sa nachádzajú naše definície.

doc = Nokogiri::HTML res
doc.css("div.def-content").map do |node|
    s = node.text.strip!
    s.gsub!(/\s{3,}/, " ") unless (s == nil)
    puts s unless (s == nil)
end

Definície sa nachádzajú v značke <div class="def-content">. Zlepšíme formátovanie výstupu tým, že odstránime nadmerné biele znaky.

$ ./get_term.rb
a small domesticated carnivore, Felis domestica or F. catus, bred in a number of varieties.
any of several carnivores of the family Felidae, as the lion, tiger, leopard or jaguar, etc.
Slang. a person, especially a man. a devotee of jazz.
a woman given to spiteful or malicious gossip.
the fur of the domestic cat.
...

Toto je skrátený výstup programu.

JSON

JSON (JavaScript Object Notation) je odľahčený formát na výmenu dát. Pre ľudí je čitateľný a pre programy ľahko generovateľný a analyzovateľný.

$ sudo gem install json

Potrebujeme naištalovať modul json.

require 'sinatra'
require 'json'

get '/example.json' do
    content_type :json
    { :name => 'Jane', :age => 17 }.to_json
end

Sinatra aplikácia vracia dáta vo formáte JSON. Na ich generovanie využije metódu to_json.

#!/usr/bin/ruby

require 'net/http'
require 'json'

uri = URI 'http://localhost:4567/example.json'
res = Net::HTTP.get uri

data = JSON.parse res

puts data["name"]
puts data["age"]

Tento skript číta JSON dáta zaslané Sinatra aplikáciou. Na spracovanie JSON dát sa použije metóda JSON.parse.

$ ./parse_json.rb
Jane
17

Toto je výstup programu parse_json.rb.

Ďalej pošleme JSON dáta Ruby programom Sinatra aplikácii.

require 'sinatra'
require 'json'

post '/readjson' do
    data = JSON.parse request.body.read
    "#{data["name"]} is #{data["age"]} years old"
end

Aplikácia spracuje prijaté JSON dáta a zašle ich späť klientovi v textovej správe.

#!/usr/bin/ruby

require 'net/http'
require 'json'

uri = URI 'http://localhost:4567/readjson'

req = Net::HTTP::Post.new uri.path, initheader = {'Content-Type' =>'application/json'}
req.body = {:name => 'Jane', :age => 17}.to_json

res = Net::HTTP.start(uri.hostname, uri.port) do |http|
    http.request req
end

puts res.body

Tento skript pošle JSON dáta Sinatra aplikácii a zobrazí jej odpoveď.

req = Net::HTTP::Post.new uri.path, initheader = {'Content-Type' =>'application/json'}

V hlavičke HTTP požiadavky musíme uviesť typ obsahu 'application/json'.

$ ./post_json.rb
Jane is 17 years old

Toto je výstup príkladu.

Autentifikácia

Modul net/http obsahuje metódu basic_auth na vykonanie základnej HTTP autentifikácie.

$ sudo gem install sinatra-basic-auth

Pre Sinatru nainštalujeme modul sinatra-basic-auth.

require 'sinatra'
require "sinatra/basic_auth"

authorize do |username, password|
    username == "user7" && password == "7user"
end

get '/' do
    "hello"
end

protect do
    get "/secure" do
        "This is restricted area"
    end
end

V Sinatra aplikácii vykonáme autorizáciu a špecifikujeme cestu, ktorá bude vyžadovať autentifikáciu; v našom prípade: /secure.

#!/usr/bin/ruby

require 'net/http'

uri = URI 'http://localhost:4567/secure'

req = Net::HTTP::Get.new uri.path
req.basic_auth 'user7', '7user'

res = Net::HTTP.start uri.hostname, uri.port do |http|
    http.request req
end

puts res.body

Skript sa pokúsi o prístup k zabezpečenému zdroju.

req = Net::HTTP::Get.new uri.path
req.basic_auth 'user7', '7user'

Pomocou metódy basic_auth uvedieme meno a heslo používateľa.

bitcoin_skoleni

$ ./credentials.rb
This is restricted area

Prístup k zabezpečenému zdroju sa nám podaril.

Zdroje

V tomto článku sme si uviedli základné postupy pre prácu s Ruby modulom Net::HTTP. Aplikácie na strane servera sme si vytvorili pomocou Ruby frameworku Sinatra. V angličtine vyšiel tento článok na autorovej webovej stránke.

Autor článku

Od roku 2006 sa venujem písaniu o počítačových technológiách, predovšetkých programovacím jazykom, grafickému užívateľskému rozhraniu a databázam.