Чтение Instagram JSON с помощью контроллера Ruby и переход к просмотру, используя HTTParty gem

#ruby-on-rails #json #http #methods #httparty

#ruby-on-rails #json #http #методы #httparty

Вопрос:

Я пытаюсь научиться обрабатывать JSON с помощью Ruby. Я прошел довольно много уроков, но запутался еще больше, поэтому я пытаюсь учиться, выполняя это.

Здесь я пытаюсь получить данные пользователя из Instagram и отобразить в моем представлении. У меня есть доступ к приведенному ниже JSON, но как мне получить доступ к определенным полям и имени пользователя или циклическим записям?

 # users_controller.rb
require 'httparty'

class UsersController < ApplicationController
    include HTTParty

    def show
        @user = User.find(params[:id])
        fetch_instagram("elonofficiall")
    end

    private
    def fetch_instagram(instagram_username)
        url = "https://www.instagram.com/#{instagram_username}/?__a=1"
        @data = HTTParty.get(url)
        return @data
    end
end
  

 # show.html.erb
# the code below is just to try if I get anything
<%= @data %>
<% @data.each do |data| %>
  <p><%= data %></p>
<% end %>
  

https://www.instagram.com/elonofficiall/?__a=1

введите описание изображения здесь

Ответ №1:

Во-первых, не выполняйте HTTP-вызовы прямо с вашего контроллера.

Вместо этого создайте отдельный класс, который «общается» с Instagram API:

 # app/clients/instagram_client.rb
class InstagramClient
  include HTTParty
  base_uri 'https://www.instagram.com'
  format :json

  def initialize(**options)
    @options = options
  end

  def user(username, **opts)
    options = @options.reverse_merge(
      '__a' => 1 # wtf is this param?
    ).reverse_merge(opts)

    response = self.class.get("/#{username}", options)
    if response.success?
      extract_user(response)
    else
      Rails.logger.error("Fetching Instagram feed failed: HTTP #{response.code}")
      nil
    end
  end

  private 
  def extract_user(json)
    attributes = response.dig('graphql', 'user')amp;.slice('id', 'biography')
    attributes ? InstagramUser.new(attributes) : nil
  end
end
  

И класс, который нормализует ответ API для использования в вашем приложении:

 # app/models/instagram_user.rb
class InstagramUser
  include ActiveModel::Model
  include ActiveModel::Attributes
  attribute :id
  attribute :biography
  attribute :username
  # etc
end
  

Это просто прямая модель Rails, которая не сохраняется в базе данных. ActiveModel::Model и ActiveModel::Attributes позволяют передавать хэш атрибутов для массового назначения точно так же, как вы делаете с моделями, поддержанными ActiveRecord.

Это дает вам объект, который вы можете просто протестировать с помощью:

 InstagramClient.new.feed('elonofficiall')
  

И вы бы интегрировали его в свой контроллер следующим образом:

 class UsersController < ApplicationController
  def show
    @user = User.find(params[:id])
    fetch_instagram(@user.instagram_username)
  end

  private
  def fetch_instagram(instagram_username)
    @instagram_user = InstagramClient.new.user(@user.instagram_username)
  end
end
  

Смешивание HTTParty с вашим контроллером — прямо плохая идея, поскольку контроллеры сложно тестировать, а контроллер уже отвечает за ответы на запросы клиентов и не нуждается в большем.

Кроме того, обработка «необработанного» ответа JSON в вашем представлении не является хорошей практикой, поскольку это создает жесткую связь между внешним API и вашим приложением и значительно усложняет ваши представления. Сначала нормализуйте данные в отдельном объекте.

 <% if @instagram_user %>
  <p><%= @instagram_user.username %></p>
<% end %>
  

Если вы действительно хотите получить мультимедийные элементы из Instagram-ленты пользователей, вам нужно сделать еще один HTTP-запрос к GET https://graph.facebook.com/{ig-user-id}/media . Смотрите официальную документацию API.

Комментарии:

1. Спасибо вам за подробное сообщение, приятель, это очень ценится! Мне здесь есть чему поучиться : )

Ответ №2:

Лучше всего убрать это из Rails в первую очередь. Это только запутывает ситуацию. Многие разработчики отвечают Rails === Ruby . Это не так.

Напишите простой скрипт ruby и протестируйте его:

 rec = HTTParty.get(url)
puts rec.headers                    # content-type is 'text/html' and should be 'application/json'
res = rec.parsed_response           # getting error
puts res.class
  

Проблема Instagram в том, что я думаю, что он навязывает пользователю плохие content-header отзывы. Это может быть уровень безопасности. Кажется, я не могу принудительно ввести ответ в json или hash либо. Так вот почему httparty у меня это не работает.