Chào các bạn! Hôm nay mình sẽ giới thiệu với các bạn 5 cách để làm việc với HTTP requests trong Ruby.
Trước khi bắt đầu, mình muốn thổ lộ với các bạn là mình cực kỳ vui khi viết bài hướng dẫn này. Ruby on Rails là một Framework mình rất thích. Có một sự thật là ai cũng vui vẻ khi làm điều mình thích đúng không? Và như một người bị ám ảnh, mình có xu hướng luôn sử dụng những thứ mình yêu thích.
Trong bài này mình sẽ hướng dẫn các bạn viết và thực thi với file .rb mà chẳng cần phải dùng những thứ phức tạp như Rails mà vẫn có thể hoạt động tốt trong bất kỳ môi trường nào. Tiếp đó mình sẽ hướng dẫn tạo file .rb của riêng bạn, điều đó không có nghĩa nó chỉ hoạt động trên local đâu. Nó cũng có thể áp dụng trong bất kỳ framework nào được viết bằng Ruby đấy.
Requirements:
Để follow bài hướng dẫn này bạn cần:
- Install Ruby. Mình sử dụng version 3.0.0 và dùng rvm để quản lý version của ruby.
- Text editor. Tham khảo: Sublime text, VS code, vim
Thu hoạch:
Chúng ta sẽ thực hiện 2 requests GET và POST bằng 5 phương pháp khác nhau bằng cách sử dụng Ruby "thuần túy". Vì vậy đối với mỗi request, chúng ta sẽ tạo một file .rb và thực thi nó trong console bằng cách sử dụng command như:
$ ruby file.rb
Vậy 5 phương pháp đó là gì?
Chúng ta sẽ điểm qua các thư viện ruby sau:
GET requests
Ở đây mình sử dụng API NASA's picture of the day, APOD.
API gửi response dưới dạng JSON với các requests thành công có http status là 200. Sau đó mình sẽ get và hiển thị body của response trong console.
Lưu ý về API keys: Ở đây mình sẽ sử dụng DEMO_KEY do NASA cung cấp vì thế mình sẽ chỉ tạo một số requests mà không sử dụng hết các APIs. Nếu bạn định sử dụng nhiều hơn, bạn có thể cân nhắc để lấy API key cho riêng mình (tham khảo tại đây).
{
"date": "2019-07-20",
"explanation": "Have you seen a panorama from another world lately? ...",
"hdurl": "https://apod.nasa.gov/apod/image/1907/a11pan1040226lftsm.jpg",
"media_type": "image",
"service_version": "v1",
"title": "Apollo 11 Landing Panorama",
"url": "https://apod.nasa.gov/apod/image/1907/a11pan1040226lftsm600.jpg"
}
POST requests
Đối với các requests POST, mình sẽ tạo các articles bằng cách sử dụng JSONPlaceholder, một công cụ demo cho các API requests.
Chúng ta sẽ mô phỏng việc tạo article với title foo
, body bar
và set userID
là 1. Tất nhiên đây là data fake phục vụ cho mục đích học tập thôi.
{
"title": "foo",
"body": "bar",
"userID": "1",
"id": 101
}
1. Chuẩn: net/HTTP
Net/HTTP là một class Ruby mặc định, nó thường được sử dụng cùng với URI.
1.1. Request GET cơ bản:
# nethttp.rb
require 'uri'
require 'net/http'
uri = URI('https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY')
res = Net::HTTP.get_response(uri)
puts res.body if res.is_a?(Net::HTTPSuccess)
$ ruby nethttp.rb
OK, ở đây mình cho URI biết URL nào mình muốn truy vấn - đây là một URL vì https://
được dùng - sau đó sử dụng method get_response
từ thư viện Net::HTTP
.
Cuối cùng, mình sẽ kiểm tra xem request có thành công hay không và in kết quả trong console nếu đúng.
Separated params
# nethttp2.rb
require 'uri'
require 'net/http'
uri = URI('https://api.nasa.gov/planetary/apod')
params = { :api_key => 'your_api_key' }
uri.query = URI.encode_www_form(params)
res = Net::HTTP.get_response(uri)
puts res.body if res.is_a?(Net::HTTPSuccess)
Ở đây mình làm tương tự như trước nhưng mình chuyển các params riêng biệt dưới dạng một hash. Điều này rất hữu ích khi bạn cần lấy các params từ các sources khác nhau hoặc khi chúng trở nên nhiều hơn. Hash là một object được sử dụng rộng rãi trong Ruby và dễ định dạng để làm cho code dễ đọc.
Khi chúng ta đã xác định các params của mình, chúng ta set các uri.query
cho params thành các params mà chúng ta mã hóa ở định dạng tương thích với web dưới dạng form sử dụng encode_www_form
. Và đó là sự khác biệt.
1.2. POST request
# nethttp3.rb
require 'uri'
require 'net/http'
uri = URI('https://jsonplaceholder.typicode.com/posts')
res = Net::HTTP.post_form(uri, 'title' => 'foo', 'body' => 'bar', 'userID' => 1)
puts res.body if res.is_a?(Net::HTTPSuccess)
Đối với request này, mình sử dụng method post_form
của Net::HTTP
, sau uri
mình truyền các giá trị cho params dưới dạng key value. Sau đó, mình in nội dung response nếu requests thành công.
Đừng ngần ngại đọc docs của chúng nhé, đây là cheat sheet bạn có thể tham khảo nhanh. Đối với cách sử dụng phức tạp, bạn có thể sử dụng OpenURI wrapper cũng là thư viện hữu ích khác.
2. httparty: Vui vẻ lên nào!
httparty là một loại gem phổ biến để thực hiện các requests.
Cách cài đặt:
$ gem install httparty
2.1 GET request
# httparty.rb
require 'httparty'
response = HTTParty.get('https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY')
puts response.body if response.code == 200
Đối với requests này, mình chỉ cần cho httparty biết method mình muốn sử dụng là get
và mình cung cấp cho nó URL đầy đủ.
Response object do httparty cung cấp chứa rất nhiều method hữu ích. Ở đây, mình sử dụng method code
để kiểm tra xem requests có thành công hay không và nếu có, mình sử dụng method body
để in phần JSON body mà API của NASA đã gửi.
Còn rất nhiều thứ khác, chẳng hạn response.parsed_response
sẽ cung cấp cho bạn parsed JSON.
2.2. POST request, the classy way!
Một cách triển khai được sử dụng rộng rãi nhưng hoàn toàn optional là implement gem để tạo một class từ nó. Và chúng ta cũng có thể làm điều đó chỉ trong một file .rb!
# httparty2.rb
require "httparty"
class PostManager
include HTTParty
base_uri 'https://jsonplaceholder.typicode.com'
def initialize()
end
def create_post(title, body, user_id)
params = { body: { title: title, body: body, userID: user_id } }
self.class.post("/posts", params).parsed_response
end
end
post_manager = PostManager.new()
p post_manager.create_post("foo", "bar", 1)
Ở đây, mình tạo một class PostManager và đặt base_uri thành base URL của API mà mình muốn thực hiện requests.
Class này có một method create_post
nhận 3 tham số. title, body và userID tương tự phần trước.
Sau đó, mình sử dụng method self.class.post
, truyền vào các parameters nhận được từ việc khởi tạo method.
Sau đó, tất cả những gì chúng ta phải làm là tạo một new instance của class này: PostManager.new()
Và sau đó sử dụng method create_post
với các arguments.
Bạn có thể thấy class cũng có một method initialize
mà có thể tận dụng khi xây dựng các trường hợp sử dụng phức tạp hơn.
Bạn cũng có thể thêm các method khác. Trong ngữ cảnh này, sẽ có ý nghĩa nếu thêm một read_post
sử dụng method get
hoặc một method delete_post
sử dụng method delete
. Giới hạn là trí tưởng tượng của bạn mà thôi :v
Bonus
httparty đi kèm với CLI, điều đó cho phép, khi gem được cài đặt hãy sử dụng nó ngay từ console như sau:
$ httparty your_url
3. HTTP (Gem! Hay còn gọi là http.rb)
http.rb là một loại gem phổ biến khác.
Cách cài đặt:
$ gem install http
3.1 GET request
# http.rb
require "http"
response = HTTP.get("https://api.nasa.gov/planetary/apod", :params => {:api_key => "DEMO_KEY"})
p response.parse
Mình sử dụng method get
của HTTP
với url và pass các params vào dưới dạng hash được đặt tên là params
.
Việc sử dụng .parse
sẽ parse JSON thành một hash.
3.2. POST request
# http2.rb
require "http"
response = HTTP.post("https://jsonplaceholder.typicode.com/posts", :form => {'title' => 'foo', 'body' => 'bar', 'userID' => 1})
p response.parse
Bonus response handling
Lưu ý rằng tất cả các response đã in đều được parse với .parse
vì chúng là JSON. Nhưng có những methods hữu ích khác mà bạn có thể sử dụng để get response.
to_s
. Method Ruby “to string” sẽ chuyển tất cả response trong một string. VD:response.body.to_s
.readpartial
: Rất hữu ích để đọc từng dòng tài liệu html. VD:response.body.readpartial
. Tìm hiểu thêm tại đây.
4. The concurrent HTTPX
Điều khác biệt nhất của httpx với các gem khác là nó là HTTP2, chính điều đó khiến nó trở nên đặc biệt hơn các loại khác đấy.
Cách cài đặt:
$ gem install httpx
4.1. GET request
# httpx.rb
require "httpx"
response = HTTPX.get("https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY")
puts response.body if response.status == 200
httpx trong các chức năng cơ bản hoạt động rất dễ hiểu. Ở đây mình cung cấp URL đầy đủ với params và sử dụng method được đặt tên theo HTTP verb mà mình muốn sử dụng.
4.2. POST request
# httpx2.rb
require "httpx"
response = HTTPX.post("https://jsonplaceholder.typicode.com/posts", :json => {'title' => 'foo', 'body' => 'bar', 'userID' => 1})
puts response.body if response.status == 201
Trong requests này, mình sử dụng method post
từ HTTPX
và chuyển các params và một hash.
Mình in ra body nếu trạng thái response là 201, có nghĩa là created trong tiêu chuẩn HTTP.
Đồng thời nhiều requests GET
# httpx3.rb
require 'httpx'
base_url = "https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY"
response, response2 = HTTPX.get(base_url, base_url + "&date=2019-07-20")
puts response.body if response.status == 200
puts response2.body if response2.status == 200
Ở đây, mình lưu trữ base url của NASA API bằng API key và gọi HTTPX thực hiện hai requests get đồng thời, một requests đến url mà mình đã requests và một requests khác gửi đến cùng url với parameter bổ sung: date
. Điều này sẽ cung cấp thông tin về hình ảnh cho date cụ thể.
Sau đó, mình in trong console hai response bodies mà mình vừa retreved.
Đây là cách sử dụng cơ bản và chắc chắn có thể được mở rộng để phù hợp với nhu cầu lớn hơn.
5. Faraday
Faraday cũng là một gem được sử dụng rộng rãi.
Cách cài đặt:
$ gem install faraday
5.1. GET request
# faraday.rb
require "faraday"
response = Faraday.get("https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY")
p response.body if response.status == 200
Nếu bạn đã theo dõi từ đầu, bạn có thể đã đoán được những gì đang xảy ra. Ở đây mình sử dụng method get
của Faraday với NASA’s API url, sau đó in response body nếu status code là 200.
Bạn cũng có thể thấy trong console rằng phần body không được xử lý dưới dạng JSON mà được hiển thị dưới dạng một string.
5.2. POST request
Với URI encoded parameters
# faraday2.rb
require "faraday"
require 'uri'
params = {title: "foo", body: "bar", userID: 1}
encoded_params = URI.encode_www_form(params)
response = Faraday.post("https://jsonplaceholder.typicode.com/posts", encoded_params)
p response.body if response.status == 201
Mình định nghĩa params
là một hash. Sau đó đặt thành các encoded_params
để mã hóa tương thích với form của web bằng cách sử dụng encode_www_form
method từ URI
.
URI là một thư viện Ruby tiêu chuẩn mà chúng ta đã thấy khi sử dụng net/HTTP. Sau đó, sử dụng method Faraday.post
và in response.
Với do
block
# faraday3.rb
require "faraday"
response = Faraday.post "https://jsonplaceholder.typicode.com/posts" do |request|
request.body = URI.encode_www_form({title: "foo", body: "bar", userID: 1})
end
p response.body if response.status == 201
Một cách khác để chuyển các additional settings cho các requests Faraday của chúng ta là sử dụng do
block.
Sử dụng object được tạo trong do
block, chúng ta sử dụng method .body
để thêm các params được mã hóa bằng URI.
Có các phương pháp hữu ích khác như request.headers['Content-Type'] = 'application/json'
đặt content type của requests, hoặc request.params
.
Lý do đằng sau sự phổ biến của Faraday là middleware. Chúng cung cấp nhiều khả năng hữu ích để xử lý với authentication, định dạng xml và yml và những điều thú vị khác mà bạn có thể khám phá thêm tại đây.
Sử dụng chúng trong bất kỳ application với framework Ruby nào.
Để sử dụng các methods được đề cập ở trên trong Ruby on Rails, Sinatra, Hanami ... vv, bạn chỉ cần xóa dòng require X
và thay vào đó thêm vào Gemfile app của bạn:
gem 'X'
Đừng quên chạy $ bundle install
trong console của bạn khi bạn chỉnh sửa Gemfile nhé.
Tóm lại
Tất cả các giải pháp được nói ở đây khá tương tự cho các requests đơn giản. Chúng khác nhau ở cấp độ sử dụng nâng cao.
Hãy nhớ rằng tất cả chúng đều được thiết kế để phù hợp với nhu cầu cụ thể và các thông số kỹ thuật này được thiết kế để bạn có thể chọn lựa loại nào phù hợp với nhu cầu sử dụng.
Tất nhiên còn rất nhiều điều để nói về từng gem mà mình đã cho bạn xem ở đây và để biết về khả năng đầy đủ của từng gem, bạn nên đọc tài liệu và tự mình thử nghiệm nhé.
Ref: https://www.twilio.com/blog/5-ways-make-http-requests-ruby