12/08/2018, 16:00

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!

0