8

我整天都在想办法解决这个问题,这让我发疯。

我有两个 Rails 应用程序,ServerApp 和 ClientApp。ClientApp 使用 Her gem 通过 API 从 ServerApp 获取数据。在我需要分页信息之前,一切都很好。

这是我用来获取订单的方法(这使用 kamainari 进行分页和 ransack 进行搜索):

# 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 格式返回一个哈希数组,Her 将其用作订单集合。这很好用。

# 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": [
      { ... }
    ]
  },
  ...
]

但我需要分页信息。看起来我需要将它作为元数据与我的 json一起发送。所以我改变了对此的回应:

respond_to do |format|
  format.json do
    render json: { orders: @orders.as_json(include: :documents), metadata: 'sent' }
  end
end

这确实发送了元数据,所以在我的 ClientApp 中我可以写 @orders.metadata 并得到“发送”。但是现在我的订单嵌套在“订单”内的一个数组中,所以我需要使用@orders.orders,然后它将它视为一个数组而不是她的集合。

在做了一些阅读之后,似乎通过标题发送分页信息是很多其他人这样做的方式(我能够使用本指南在 after_filter 中设置标题)。但是我对如何在我的 ClientApp 中获取这些响应标头更加迷茫——我相信我需要一个法拉第中间件,但我只是没有运气让它工作。

如果有人知道我如何才能完成这项工作,我将不胜感激。我不能再把头撞在墙上了,但我觉得我只是解决这个问题的重要信息之一

4

3 回答 3

8

我遇到了同样的问题,并通过添加我自己的中间件并重写“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]
于 2014-02-05T16:02:33.360 回答
3

我终于得到了这个工作。诀窍是在法拉第 on_complete 中使用全局变量 - 我试图找到更好的解决方案,但这是我能做的最好的。再一次,我从这里得到了标题代码。以下是如何使用 Her 进行分页的完整指南:

首先,在我的服务器端,我有 Kaminari gem,我将pageper作为参数从客户端传递给服务器。(这也是使用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(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

现在要接收标头,我们需要客户端上的 Her 中的法拉第中间件。

# 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])`

我希望这可以帮助其他人,如果有人知道更好的方法,请告诉我!

于 2013-05-22T21:07:49.107 回答
0
于 2013-05-17T06:49:12.510 回答