Gọi API với Net::HTTP
Thông thường trong các dự án sử dụng Ruby, khi cần làm việc với các ứng dụng bên thứ 3 chúng ta sẽ tìm các gem hỗ trợ. Tuy nhiên trong nhiều trường hợp, ứng dụng bên thứ ba không hỗ trợ gem Ruby. Hoặc gem đó đã bị quá hạn, không cập nhật dẫn đến việc sai lệch dữ liệu. Trong những trường hợp đó, ...
Thông thường trong các dự án sử dụng Ruby, khi cần làm việc với các ứng dụng bên thứ 3 chúng ta sẽ tìm các gem hỗ trợ. Tuy nhiên trong nhiều trường hợp, ứng dụng bên thứ ba không hỗ trợ gem Ruby. Hoặc gem đó đã bị quá hạn, không cập nhật dẫn đến việc sai lệch dữ liệu. Trong những trường hợp đó, luôn luôn có một cách chúng ta cần nghĩ tới, cũng là cách dễ dàng: Sử dụng trực tiếp API đó thông qua Net::HTTP.
Net::HTTP cung cấp một thư viện rất mạnh, nó có thể sử dụng để xây dựng các HTTP user-agent. Net::HTTP được thiết kế để làm việc được với URI. URI::HTTP#host, URI::HTTP#port và URI::HTTP#request_uri được thiết kế để làm việc được với Net::HTTP.
Một vài ví dụ với Net::HTTP
GET
Net::HTTP.get('example.com', '/index.html') # => String
GET bởi URI
uri = URI('http://example.com/index.html?count=10') Net::HTTP.get(uri) # => String
GET bởi những tham số động
uri = URI('http://example.com/index.html') params = { :limit => 10, :page => 3 } uri.query = URI.encode_www_form(params) res = Net::HTTP.get_response(uri) puts res.body if res.is_a?(Net::HTTPSuccess)
POST
uri = URI('http://www.example.com/search.cgi') res = Net::HTTP.post_form(uri, 'q' => 'ruby', 'max' => '50') puts res.body
POST cùng với nhiều giá trị
uri = URI('http://www.example.com/search.cgi') res = Net::HTTP.post_form(uri, 'q' => ['ruby', 'perl'], 'max' => '50') puts res.body
Ví dụ thực tế khi gọi API Yamato
Yamato Japan Tracking API cho phép chúng ta tạo yêu cầu gửi hàng, cập nhật trạng thái giao hàng. Ứng dụng này rất hữu hiệu với các cửa hàng bán hàng online, cần giao hàng tới tận nhà khách hàng.
Yamato API gửi và nhận dữ liệu dưới định dạng xml. Như vậy trước khi gửi một API POST để tạo yêu cầu gửi hàng, chúng ta cần có bước xử lý dữ liệu gửi lên. Trong Ruby/Rails có gem Nokogiri rất hữu ích cho việc xử lý dữ liệu xml này. (Cụ thể sẽ được trình bày ở source code ví dụ). Tóm lại, trong ví dụ cụ thể này chúng ta sẽ cần thực hiện các bước sau:
+) Tiền xử lý dữ liệu gửi lên với định dạng xml +) Sử dụng Net::HTTP để gọi tới API Yamato chính xác theo tài liệu mô tả. +) Xử lý dữ liệu xml được trả về.
Cụ thể theo tài liệu phía Yamato mô tả thì sẽ có rất nhiều dữ liệu cần được gửi lên, cũng như có nhiều cấu trúc dữ liệu được trả về. Sau đây tôi sẽ trình bày phần mã nguồn cơ bản nhất theo 3 bước trên.
Bước 1
def request_data order namespaces = {"xmlns:soap" => "http://schemas.xmlsoap.org/soap/envelope/"} Nokogiri::XML::Builder.new(encoding: "utf-8") do |xml| xml["soap"].Envelope("xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance", "xmlns:soap": "http://schemas.xmlsoap.org/soap/envelope/") do xml["soap"].Body do xml.RequestShipment(xmlns: Settings.yamato_api.xmlns) do xml.id ENV["YAMATO_ID"] xml.pw ENV["YAMATO_PW"] xml.DataCount 1 xml.Shipment_List do xml.Shipment_Data do xml.DataType 0 xml.UnsoIraiNo order.id xml.SinCode settings.sincode xml.SinJyutyuDate Date.today.to_s.gsub(///, "") xml.SinDataCreateDate Date.today.to_s.gsub(///, "") .... end end end end end
Bước 2
def api_request order settings = Rails.env.production? ? Settings.yamato_api.production : Settings.yamato_api.test api_url = settings.request_shipment.url uri = URI.parse api_url http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true http.verify_mode = OpenSSL::SSL::VERIFY_PEER request = Net::HTTP::Post.new(uri.path, {'Content-Type' => 'text/xml'}) request.add_field("SOAPAction", settings.request_shipment.soap_action) request.body = request_data order, settings http.request request end
Bước 3
Dữ liệu trả về cũng có đẩy đủ các phần code, message, body. Phần body định dạng xml có thể được parse cũng bởi Nokogiri, bạn có thể gọi tới từng phần tử của chuối xml bời cú phát .path("./ElementName")
response = api_request order data = Nokogiri::XML response.body data.remove_namespaces! error_code = data.xpath("//RequestShipmentResult/ErrorCode").inner_text .....
Trên đây mình đã giới thiệu về Net::HTTP cơ bản và một ứng dụng hoàn chỉnh điển hình của nó. Bên cạnh đó nó cũng còn được sử dụng trong nhiều mục đích khác nữa. Các bạn có thể tìm hiểu thêm tại link sau, mình cũng đã dùng link đó để tham khảo cho bài viết này https://ruby-doc.org/stdlib-2.4.1/libdoc/net/http/rdoc/Net/HTTP.html
Cảm ơn các bạn đã quan tâm!