

## やりたいこと

  * Rails の Book モデルから google books api を叩いて書籍情報のレスポンスを取得したい

## 発生したエラー

[code]

    web_1        | Errno::EADDRNOTAVAIL (Failed to open TCP connection to :80 (Address not available - connect(2) for nil port 80)):
    web_1        |   
    web_1        | app/models/book.rb:24:in `search'
    web_1        | app/controllers/books_controller.rb:3:in `search'
    
[/code]

## コード

フォームから送られてきたキーワード（書籍名や著者など）を使って API を叩きます。

[code]

    class BooksController < ApplicationController
      def search
        @results = Book.search(params[:book_search][:search_key_word])
      end
    end
    
[/code]

日本語が含まれるので、 `url_encode` メソッドでエンコードしています。

[code]

    class Book < ApplicationRecord
      class << self
        def search(key_word)
          api_endpoint = ERB::Util.url_encode("https://www.googleapis.com/books/v1/volumes?q=#{key_word}&maxResults=20")
          response = Net::HTTP.get(URI.parse(api_endpoint)) # ここで上記のエラーが発生しています。
          JSON.parse(response)
        end
      end
    end
    
[/code]

同じエンドポイントに対して curl を実行すると当然適切にレスポンスが返ります。

dockerを使っていることが何か関係しているのでしょうか？  
alpine linux をベースにしています。  
<https://hub.docker.com/layers/ruby/library/ruby/2.6.6-alpine/images/sha256-95d5b6bf7084a6a0f04b48a7fc1c533ccb15e1550c670ff589ef56e620f40286?context=explore>

## 環境

  * docker 2.3.0.4
  * docker-compose 3.8
  * railsコンテナbaseイメージ ruby:2.6.6-alpine
  * ruby 2.6.6
  * rails 6.0.3.2

* * *

何か分かることがあればご教授頂けますと幸いです。  
よろしくお願い致します。


CC BY-SA 4.0
2020-08-16T00:47:04.760
69591
2022-03-03T00:00:40.027
36652
question
ruby-on-rails ruby docker google-api
Rails on Docker から api を叩いた際の Errno::EADDRNOTAVAIL


`ERB::Util.url_encode` が不要なのではないかと思います。

[code]

    api_endpoint = ERB::Util.url_encode("https://www.googleapis.com/books/v1/volumes?q=#{key_word}&maxResults=20")
    
[/code]

ここを

[code]

    api_endpoint = "https://www.googleapis.com/books/v1/volumes?q=#{key_word}&maxResults=20"
    
[/code]

このように変更してみてはいかがでしょうか。

* * *

[追記]

`key_word` が外部から与えられるような信頼できない値の場合は、URL 全体をエンコード(エスケープ)するのではなく、`key_word`
だけをエンコードしてそれを URL の一部として使用したほう安全です。  
その場合は `URI.encode_www_form_component` が使用できます。

例:

[code]

    encoded = URI.encode_www_form_component(key_word)
    uri = "https://www.googleapis.com/books/v1/volumes?q=#{encoded}&maxResults=20"
    
[/code]


CC BY-SA 4.0
2020-08-16T06:22:56.770
69595
2020-08-16T10:49:22.587
2020-08-16T10:49:22.587
3249
3249
69591
answer


解決しました。

原因としては、

[code]

    ERB::Util.url_encode("https://www.googleapis.com/books/v1/volumes?q=#{key_word}&maxResults=20")
    
[/code]

この部分でエンコードした時に返ってくる文字列をそのまま `URI.parse` に渡すと、`URI::Generic`
が返っていました。uriのhostもscheme もnil になっていて正しく取得できていなかったようです。  
それをそのまま Net::HTTP.get に渡しているので表題のようなエラーとなっていました。

`URI.encode` が非推奨のため、他のメソッドでエンコードしたかったです。  
最終的には、`WEBrick::HTTPUtils.#escape` を使ってエンコードした文字列を URI.parse すると URI::HTTP
のインスタンスが返り、scheme なども正しくセットされた状態となり解決しました。

ありがとうございました。

  * 参考

[code]

    def search(key_word)
    uri = URI.parse(WEBrick::HTTPUtils.escape("https://www.googleapis.com/books/v1/volumes?q=#{key_word}&maxResults=20"))
    response = Net::HTTP.get_response(uri)
    JSON.parse(response.body)
    end
    
[/code]


CC BY-SA 4.0
2020-08-16T06:51:57.143
69596
2020-08-16T06:51:57.143
36652
69591
answer