Разбиение на страницы Rails в API с помощью Her, Faraday
Я пытался выяснить это весь день, и это сводит меня с ума.
у меня есть два приложения rails, ServerApp и ClientApp. ClientApp получает данные от ServerApp через API, используя ее драгоценный камень. Все было замечательно, пока мне не понадобилась информация о разбиении на страницы.
Это метод, который я использую для получения заказов (это использует kamainari для разбиения на страницы и обыска для поиска):
# ServerApp
def search
@search = Order.includes(:documents, :client).order('id desc').search(params[:q])
@orders = @search.result(distinct: true).page(params[:page]).per(params[:per])
respond_with @orders.as_json(include: :documents)
end
он возвращает массив хэшей в json, который она использует в качестве сбор заказов. Это прекрасно работает.
# Response
[
{
"client_id": 239,
"created_at": "2013-05-15T15:37:03-07:00",
"id": 2422,
"ordered_at": "2013-05-15T15:37:03-07:00",
"origin": "online",
"updated_at": "2013-05-15T15:37:03-07:00",
"documents": [
{ ... }
]
},
...
]
но мне нужна была информация о разбиении на страницы. Похоже, мне нужно было ... --12-->отправить его в качестве метаданных с моим json. Поэтому я меняю свой ответ на это:
respond_to do |format|
format.json do
render json: { orders: @orders.as_json(include: :documents), metadata: 'sent' }
end
end
Это действительно отправляет метаданные, поэтому в моем ClientApp я могу писать @orders.метаданные и получить "отправлено". Но теперь мои заказы вложены в массив внутри "заказов", поэтому мне нужно использовать @orders.приказывает, а затем рассматривает его как массив вместо нее коллекция.
после некоторого чтения казалось, что отправка информации о разбиении на страницы через заголовки была так, как это делали многие другие люди (я смог настроить заголовки в after_filter с помощью данное руководство). Но я еще больше запутался в том, как получить эти заголовки ответов в моем ClientApp - я считаю, что мне нужно промежуточное ПО Фарадея, но мне просто не повезло, что это работает.
Если кто-нибудь знает как я могу это сделать, я буду очень благодарна. Я не могу вынести еще один день удара головой об стену, но я чувствую, что я просто один жизненно важный кусок информации от от этого решения!
3 ответов
я столкнулся с той же проблемой и решил ее, добавив свое собственное промежуточное ПО и переписав методы "parse" и "on_complete" без особых хлопот и избегая использования глобальных переменных.
вот код:
class CustomParserMiddleware < Her::Middleware::DefaultParseJSON
def parse(env)
json = parse_json(env[:body])
pagination = parse_json(env[:response_headers][:pagination_key]) || {}
errors = json.delete(:errors) || {}
metadata = json.delete(:metadata) || {}
{
:data => json,
:errors => errors,
:metadata => {
:pagination => pagination,
:additional_metadata => metadata
},
end
def on_complete(env)
env[:body] = case env[:status]
when 204
parse('{}')
else
parse(env)
end
end
end
затем вы можете получить доступ к разбиению на страницы следующим образом:
model = Model.all
model.metadata[:pagination]
я, наконец, получил это работает. Хитрость заключалась в том, чтобы использовать глобальную переменную в faraday on_complete - я пытался найти лучшее решение, но это было лучшее, что я мог сделать. Еще раз, я получил код заголовка от здесь. Вот полное руководство о том, как заставить pagination работать с ней:
во-первых, на моей стороне сервера у меня есть камень Каминари, и я прохожу page
и per
как params к серверу от клиента. (Это также использует ransack для поиск)
def search
@search = Order.order('id desc').search(params[:q])
@orders = @search.result(distinct: true).page(params[:page]).per(params[:per])
respond_with @orders.as_json(include: :items)
end
мой клиент делает запрос вот так:
@orders = Order.search(q: { client_id_eq: @current_user.id }, page: params[:page], per: 3)`
назад на сервер, у меня есть это в моем ApiController (App controller for api):
protected
def self.set_pagination_headers(name, options = {})
after_filter(options) do |controller|
results = instance_variable_get("@#{name}")
headers["X-Pagination"] = {
total_count: results.total_count,
offset_value: results.offset_value
}.to_json
end
end
на сервере orders_controller.rb, я устанавливаю заголовки страниц для метода поиска:
class OrdersController < ApiController
set_pagination_headers :orders, only: [:search]
...
end
теперь, чтобы получить заголовки, нам нужно промежуточное ПО Фарадея в ней на клиенте.
# config/initializers/her.rb
Her::API.setup url: Constants.api.url do |c|
c.use TokenAuthentication
c.use HeaderParser # <= This is my middleware for headers
c.use Faraday::Request::UrlEncoded
c.use Her::Middleware::DefaultParseJSON
c.use Faraday::Adapter::NetHttp
c.use Faraday::Response::RaiseError
end
# lib/header_parser.rb
# don't forget to load this file in application.rb with something like:
# config.autoload_paths += Dir[File.join(Rails.root, "lib", "*.rb")].each { |l| require l }
class HeaderParser < Faraday::Response::Middleware
def on_complete(env)
unless env[:response_headers]['x-pagination'].nil?
# Set the global var for pagination
$pagination = JSON.parse(env[:response_headers]['x-pagination'], symbolize_names: true)
end
end
end
теперь в вашем клиентском контроллере у вас есть глобальная переменная хэш называется $ pagination; мой выглядит так:
$pagintation = { total_count: 0, offset_value: 0 }`
наконец, я добавил Kaminari gem в мое клиентское приложение, чтобы разбить массив на страницы и получить эти простые ссылки на страницы:
@orders = Kaminari.paginate_array(@orders, total_count: $pagination[:total_count]).page(params[:page]).per(params[:per_page])`
Я надеюсь, что это может помочь кому-то еще, и если кто-нибудь знает лучший способ сделать это, дайте мне знать!
вы можете передать параметры заголовка Фарадею при настройке соединения, см. документы вhttp://rubydoc.info/gems/faraday/0.8.7/Faraday/Connection:initialize
иногда это помогает сделать
curl
запрос первый, esp. использовать для подробного вывода, где вы увидите все заголовки. (Возможно, вы также можете подключить некоторые выходы журнала с сервера)вы можете использовать например
clogger
(http://clogger.rubyforge.org/) контролируйте информацию заголовка на стороне сервера Rails