+1

Một số hướng dẫn cho việc sử dụng HTTP request trong Ruby

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

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí