Разбиение на страницы 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