Một số hướng dẫn cho việc sử dụng HTTP request trong Ruby
Bài đăng này đã không được cập nhật trong 5 năm
Khi làm việc trong dự án bạn khó tránh khỏi việc cần phải làm việc với api bên thứ 3, có thể là dịch vụ khách hàng yêu cầu hoặc dịch vụ từ bên ngoài nào đó. Ví dụ như là việc lấy thông tin từ một website nào đó hay là cần phải submit form, upload image,.... thì chúng ta cần phải gửi lên một request HTTP và xử lý response trả về.
Dưới đây sẽ hưóng dẫn cơ bản giúp bạn có thể:
- Tạo một HTTP request cơ bản sử dụng thư viện net/http
- Gửi một request SSL
- Submit form với POST request
- Cách custom headers request
- Và chọn một thư việc HTTP client tốt nhất dựa trên yêu cầu của bạn.
Bây giờ chúng ta sẽ cùng tìm hiểu từng bước một
Tạo một request HTTP cơ bản
Bản thân Ruby đã xây dựng một thư viện http client được gọi là net/http, nên khi sử dụng Ruby là bạn có sử dụng nó để gửi bất kì request http nào mà bạn mong muốn. Ví dụ
require 'net/http'
Net::HTTP.get('example.com', '/index.html')
Nó sẽ trả về cho bạn một string với nội dung là html của trang đó.
"<!doctype html>\n<html>\n<head>\n <title>Example Domain</title>\n\n <meta charset=\"utf-8\" />\n <meta http-equiv=\"Content-type\" content=\"text/html; charset=utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <style type=\"text/css\">\n body {\n background-color: #f0f0f2;\n margin: 0;\n padding: 0;\n font-family: \"Open Sans\", \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n \n }\n div {\n width: 600px;\n margin: 5em auto;\n padding: 50px;\n background-color: #fff;\n border-radius: 1em;\n }\n a:link, a:visited {\n color: #38488f;\n text-decoration: none;\n }\n @media (max-width: 700px) {\n body {\n background-color: #fff;\n }\n div {\n width: auto;\n margin: 0 auto;\n border-radius: 0;\n padding: 1em;\n }\n }\n </style> \n</head>\n\n<body>\n<div>\n <h1>Example Domain</h1>\n <p>This domain is established to be used for illustrative examples in documents. You may use this\n domain in examples without prior coordination or asking for permission.</p>\n <p><a href=\"http://www.iana.org/domains/example\">More information...</a></p>\n</div>\n</body>\n</html>\n"
Nhưng thông thường thì bạn muốn nhiều thông tin hơn chỉ có nội dung html. đơn giản như là HTTP response status, nếu không có response status thì bạn sẽ không biết được liệu request của bạn có thành công hay không. Nên thay vì đó chúng ra sẽ sử dụng phương thức get_response
[2] pry(main)> response = Net::HTTP.get_response('example.com', '/')
=> #<Net::HTTPOK 200 OK readbody=true>
[3] pry(main)> response.code
=> "200"
Qua status code trả về chúng ta hoàn toàn biết được request có thành công hay không. Và nếu giờ bạn muốn lấy được content của request bạn có thể gọi method body
của response trả về
[4] pry(main)> response.body
=> "<!doctype html>\n<html>\n<head>\n <title>Example Domain</title>\n\n <meta charset=\"utf-8\" />\n <meta http-equiv=\"Content-type\" content=\"text/html; charset=utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <style type=\"text/css\">\n body {\n background-color: #f0f0f2;\n margin: 0;\n padding: 0;\n font-family: \"Open Sans\", \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n \n }\n div {\n width: 600px;\n margin: 5em auto;\n padding: 50px;\n background-color: #fff;\n border-radius: 1em;\n }\n a:link, a:visited {\n color: #38488f;\n text-decoration: none;\n }\n @media (max-width: 700px) {\n body {\n background-color: #fff;\n }\n div {\n width: auto;\n margin: 0 auto;\n border-radius: 0;\n padding: 1em;\n }\n }\n </style> \n</head>\n\n<body>\n<div>\n <h1>Example Domain</h1>\n <p>This domain is established to be used for illustrative examples in documents. You may use this\n domain in examples without prior coordination or asking for permission.</p>\n <p><a href=\"http://www.iana.org/domains/example\">More information...</a></p>\n</div>\n</body>\n</html>\n"
Bản thân net/http là một class được xây dựng trong thư viện Ruby luôn nên việc sử dụng nó, cũng như những tính năng mà nó đưa ra cũng khá đơn giản. Tuy nhiên có rất nhiều gems có thể giúp chúng ta làm mọi việc dễ dàng hơn. trong đó có httparty gem
Sử dụng HTTParty Gem để tạo request
require 'httparty'
response = HTTParty.get('http://example.com')
response.code
# => 200
response.body
=> <!doctype html>\n<html>\n<head>\n <title>Example Domain</title>\n\n <meta charset=\"utf-8\" />\n <meta http-equiv=\"Content-type\" content=\"text/html; charset=utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <style type=\"text/css\">\n body {\n background-color: #f0f0f2;\n margin: 0;\n padding: 0;\n font-family: \"Open Sans\", \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n \n }\n div {\n width: 600px;\n margin: 5em auto;\n padding: 50px;\n background-color: #fff;\n border-radius: 1em;\n }\n a:link, a:visited {\n color: #38488f;\n text-decoration: none;\n }\n @media (max-width: 700px) {\n body {\n background-color: #fff;\n }\n div {\n width: auto;\n margin: 0 auto;\n border-radius: 0;\n padding: 1em;\n }\n }\n </style> \n</head>\n\n<body>\n<div>\n <h1>Example Domain</h1>\n <p>This domain is established to be used for illustrative examples in documents. You may use this\n domain in examples without prior coordination or asking for permission.</p>\n <p><a href=\"http://www.iana.org/domains/example\">More information...</a></p>\n</div>\n</body>\n</html>\n
Những lợi ích khi sử dụng gem này:
- Sử dụng dễ dàng hơn
- Status code được trả về ngay trong method get mà không cần đến
get_response
- Việc sử dụng SSL đơn giản, chỉ cần thay đổi url là được. (Khác với net/http mà chúng ta sẽ tìm hiểu dưới)
http://foo.com => no SSL
https://foo.com => SSL
http://foo.com:443 => SSL
Gửi một request SSL
Nếu như bạn muốn gửi một request ssl với net/http, thì bạn sẽ nhận ngay được lỗi kết nối.
[7] pry(main)> Net::HTTP.get_response("example.com", "/", 443)
Errno::ECONNRESET: Connection reset by peer
from <internal:prelude>:74:in `__read_nonblock'
Để làm được điều này thì bạn cần phải thông qua 3 bước.
require "net/https"
require "uri"
uri = URI.parse("https://secure.com/")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Get.new(uri.request_uri)
response = http.request(request)
response.code
=> "301"
response.body
=> "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n<html><head>\n<title>301 Moved Permanently</title>\n</head><body>\n<h1>Moved Permanently</h1>\n<p>The document has moved <a href=\"http://www.secure.com/\">here</a>.</p>\n<hr>\n<address>Apache/2.2.15 (CentOS) Server at secure.com Port 443</address>\n</body></html>\n"
Thay vì làm nhiều bước như thế này thì hãy sử dụng HTTParty như đã được nói đến với lợi ích thứ 3 ở trên kia. Giúp chúng ta sử dụng dễ dàng hơn rất nhiều.
Submit form với POST request
Thông thường một GET request chỉ lấy được thông tin từ 1 website như là lấy content của website, downloading image, css, javascript file,... Nên nếu bạn muốn submit 1 thông tin nào đó thì cần sử dụng method POST
Ví dụ
HTTParty.post("http://example.com/login", body: { user: "test@example.com", password: "Aa@123456" })
Và để submit 1 file thì cần phải dùng multipart request, và nó không được support bởi HTTParty gem, chúng ta sẽ sử dụng rest-client gem hoặc faraday gem
require 'rest-client'
RestClient.post '/profile', file: File.new('photo.jpg', 'rb')
require 'faraday'
conn =
Faraday.new do |f|
f.request :multipart
f.request :url_encoded
f.adapter :net_http
end
file_io = Faraday::UploadIO.new('photo.jpg', 'image/jpeg')
conn.post('http://example.com/profile', file: file_io)
Tạo request với custom headers
Bạn hoàn toàn có thể gửi request với headers mình mong muốn, điều này giúp bạn có thể gửi thêm một số thông tin lên request như là cookies, user-agent hay thông tin về caching. Cùng xem ví dụ về việc custome headers
Faraday.new('http://example.com', headers: { 'User-Agent' => 'test' }).get
Bạn có thể truyền bất cứ biến nào của header đều được.
Thống kê một số thư viện HTTP client
Có rất nhiều thư viện HTTP trong Ruby mà chúng ta có thể dùng, nhưng nên chọn cái nào để dùng? Chúng ta hãy cùng nhau đưa ra một vài thông số cho các thư viện đó.
Repo | Stars | Latest release | Issues |
---|---|---|---|
lostisland/faraday | 4119 | 23/05/2018 | 21 |
rest-client/rest-client | 4454 | 5/6/2017 | 71 |
typhoeus/typhoeus | 3347 | 21/8/2017 | 74 |
jnunemaker/httparty | 4609 | 30/03/2018 | 41 |
excon/excon | 898 | 27/03/2018 | 17 |
httprb/http | 2245 | 25/04/2018 | 45 |
Dựa vào những thông tin trên thì trước tiên hãy nhìn vào mức độ popularity và quá trình maintained (latest release) sau đó bạn hãy nhìn vào những issues mà nó đang có, đó là bước đầu tiên để có thể xem xét 1 lựa chọn.
Và hầu hết những gem trên đều xây dựng trên thư viện net/http của ruby riêng có faraday và typhoeus là khác chút.
Faraday được xây dựng như một apdapter (có thể tạo request với net/http
, rest-client
...) bằng việc config adapter như chúng ta đã thấy trên.
Còn Typhoeus được xây dựng trên nền tảng C, giúp cho việc Multi-Threading request.
require 'typhoeus'
hydra = Typhoeus::Hydra.hydra
request = Typhoeus::Request.new("http://www.rubyguides.com")
hydra.queue(request)
hydra.queue(request)
hydra.run
Qua trên bạn đã biết được cách tạo 1 request http trong Ruby để có thể dùng liên kết với 1 service thứ 3 nào đó, đồng thời có thể thấy 1 số thông số so sánh giữa các thư viện hiện có đẻ giúp bạn có thể chọn được thư viện tốt nhất so với yêu cầu.
Reference: https://www.rubyguides.com/2018/08/ruby-http-request/
All rights reserved