#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 %>
Ответ №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
у меня это не работает.