LoginSignup
12
7

More than 1 year has passed since last update.

【Rails】入力された値を元にGETリクエストを送ってデータを取得し、DBに保存する

Last updated at Posted at 2020-02-28

すること

今回はGET statuses/oembed — Twitter Developersを用いてツイッターカードを表示します。

埋め込みHTMLを取得するためには認証は必要ないので、単純なリクエストでデータを取得することが可能です。

流れは以下の通りです。

  1. 入力されたURLからIDを取得
  2. リクエストを送信してデータを取得
  3. URL, jsonをデータベースに保存
  4. 保存したデータを表示

開発環境

Ruby 2.6.5
Rails 5.2.3
MySQL 8.0.19

コントローラ

別段珍しいことはしていません。
redirect_to, renderは適当です。

tweets_controller.rb
class TweetsController < ApplicationController
  def create
    if @tweet.create(tweet_params)
      redirect_to tweet_path(@tweet)
    else
      render :new
    end
  end

  def update
    @tweet = Tweet.find(params[:id])
    if @tweet.update(tweet_params)
      redirect_to tweet_path(@tweet)
    else
      render :edit
    end
  end

  private

  def tweet_params
    params.require(:tweet).permit(:url)
  end
end

モデル

事前に、jsonを入れる予定のt_jsonカラムをマイグレーションで作成しておきます。
(追記)jsonを1カラムを入れるのは、変更やデータの取得がしづらいためアンチパターンと言われています。使う際は注意してください。

tweet.rb
# == Schema Information
#
# Table name: tweets
#
#  id         :bigint           not null, primary key
#  url        :string(255)      not null
#  t_json     :json             not null
#  created_at :datetime         not null
#  updated_at :datetime         not null
#

class Tweet < ApplicationRecord
  validates :url, presence: true, length: { maximum: 200 }
end

このままでは、入力したurlしか保存されません。ということで、urlからt_jsonを取得し保存するまでの流れを順に追っていきます。

1. 入力されたURLからIDを取得

URLは、例えばhttps://twitter.com/Interior/status/507185938620219395という形になりますが、リクエストに必要なのはIDの507185938620219395の部分だけです。
ということで、入力されたURLからIDとなる文字列を抜き出すtweet_idというインスタンスメソッドを作成します。

  def tweet_id
    url.split('/').last
  end

ちなみに、tweet.tweet_idというような書き方で、モデルの外部からもIDを取得できます。

2. リクエストを送信してデータを取得

TwitterAPIにGETリクエストを送ります。
curlコマンドでは以下のようなコマンドになります。

ExampleRequest
$ curl 'https://publish.twitter.com/oembed?url=https%3A%2F%2Ftwitter.com%2FInterior%2Fstatus%2F507185938620219395'

これをRubyで実装します。2つの方法を挙げます。

open_uriを用いる方法

  def tweet_json
    url = "https://publish.twitter.com/oembed?url=https%3A%2F%2Ftwitter.com%2FInterior%2Fstatus%2F#{tweet_id}"
    responce = OpenURI.open_uri(url)  # リクエストを送り、返り値をresponceに代入
    ActiveSupport::JSON.decode(responce.read)
  end

Net::HTTPを用いる方法

  def tweet_json
    url = "https://publish.twitter.com/oembed?url=https%3A%2F%2Ftwitter.com%2FInterior%2Fstatus%2F#{tweet_id}"
    uri = URI.parse(url)
    responce = Net::HTTP.get(uri)  # リクエストを送り、返り値をresponceに代入
    JSON.parse(responce)
  end

Ruby の HTTP リクエストを送る方法の性能比較によると、Net::HTTPの方が速いようです。

これで以下のようなjson形式のデータを返すメソッドを作成できました。

{
  "url": "https://twitter.com/Interior/status/507185938620219395",
  "author_name": "US Dept of Interior",
  "author_url": "https://twitter.com/Interior",
  "html": "<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Happy 50th anniversary to the Wilderness Act! Here&#39;s a great wilderness photo from <a href="https://twitter.com/YosemiteNPS">@YosemiteNPS</a>. <a href="https://twitter.com/hashtag/Wilderness50?src=hash">#Wilderness50</a> <a href="http://t.co/HMhbyTg18X">pic.twitter.com/HMhbyTg18X</a></p>&mdash; US Dept of Interior (@Interior) <a href="https://twitter.com/Interior/status/507185938620219395">September 3, 2014</a></blockquote>n<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>",
  "width": 550,
  "height": null,
  "type": "rich",
  "cache_age": "3153600000",
  "provider_name": "Twitter",
  "provider_url": "https://twitter.com",
  "version": "1.0"
}

3. 取得したjsonをtweet.t_jsonに格納して保存

tweetを保存する前に、before_saveを用いてt_jsonに値を入れることにします。
set_jsonメソッドの役割は、URLが変更されていた場合、t_jsonに値を入れるというものです。
外部APIを叩きすぎるのはよくないので、URLが変わっていない場合は特に変更しません(今回の例では他に変更しうるカラムがないので別に必要ないのですが)。

tweet.rb
class Tweet < ApplicationRecord
  before_save :set_json # 追加

  def set_json
    self.t_json = tweet_json if will_save_change_to_url?
  end

  def tweet_json
    # TwitterAPIから取得したJSONデータを返す
  end
end

最終的なモデル

tweet.rb
class Tweet < ApplicationRecord
  before_save :set_json
  validates :url, presence: true, length: { maximum: 200 }

  def tweet_id
    url.split('/').last
  end

  private  # 以下のメソッドは外部から呼び出さないのでprivateにする

  def set_json
    self.t_json = tweet_json if will_save_change_to_url?
  end

  def tweet_json
    url = "https://publish.twitter.com/oembed?url=https%3A%2F%2Ftwitter.com%2FInterior%2Fstatus%2F#{tweet_id}"
    uri = URI.parse(url)
    responce = Net::HTTP.get(uri)
    JSON.parse(responce)
  end

ビュー

Twitterカードを表示するにはjsonの"html"をそのままHTMLとして出力すればいいので、ビューファイルで以下のように記述します。

view.html.erb
<%= @tweet.t_json['html'].html_safe %>

ツイート情報を取得する際のオプション

"https://publish.twitter.com/oembed?url=https%3A%2F%2Ftwitter.com%2FInterior%2Fstatus%2F#{tweet_id}&omit_script=true"

このように&でつなげます。(パラメータの種類については公式ページ参照)
omit_script=trueにすると、<script>部分を除いたHTMLが返されるので、1ページに複数のツイートを表示する場合はおすすめです。

view.html.erb
<% @tweets.each do |tweet| %>
  <%= tweet.t_json['html'].html_safe %>
<% end %>

<%= javascript_include_tag '//platform.twitter.com/widgets.js' %>

リンク

例外処理など細かく行う場合

12
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
7