在 Rails 5 API 中管理多个 GET 参数 [英] Managing multiple GET parameters in Rails 5 API

查看:54
本文介绍了在 Rails 5 API 中管理多个 GET 参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 Rails 5 API 构建一个用于检索数据的应用程序.我想使用多个 GET 参数来提取数据.

示例 GET 请求格式

/users?company=Samsung&position=Engineer

单个参数的控制器代码:-

def 索引client = User.where("company = ?", params[:company])渲染json:客户端结尾

多个参数的控制器代码:-

def 索引client = User.where("company = ?", params[:company]).where("position = ?", params[:position])渲染json:客户端结尾

<块引用>

同样,GET 请求中可能包含/可能不包含许多参数.

我想要的

如果用户未指定任何参数,则应显示所有用户.如果只指定了几个参数,则只搜索那些参数.

发生了什么

如果用户在 GET 请求中只指定了位置参数,Rails 无法处理它&显示一个空白的 json,因为没有为其指定规则.

<块引用>

请注意,有很多参数,不可能为每个参数组合都编写规则.

解决方案

您的解决方案的基本问题是它迫使每个地方依次执行.这意味着如果 company 参数是 nil 你会得到一个空的关系,之后的所有 where 语句都将返回一个空的关系.只有提供了参数才需要进行条件过滤.

您要做的最好在查询/搜索或过滤器对象中完成(查询采用参数并返回结果集,过滤器采用已生成的集合并将其过滤掉).我喜欢在 app/services/ 中定义这些,尽管有些人将它们放在 app/queries 中.

但是,您的方法存在一些不一致之处,索引端点应返回对象列表.即使您希望获得一个选项,您也应该期望可能有多个选项.假设我们修复了您的索引端点并使用了过滤器对象.

# app/controllers/users_controller.rb定义索引用户 = 用户.where(1=1)users = Users::Filter.call(users, params)渲染json:用户结尾

你会注意到我使用 User.where(1=1),这是因为我习惯于处理 rails 3,如果你使用 User.all.

#/app/services/users/filter.rb类用户::过滤器def self.call(资源,选项)新的(用户,选项).过滤器结尾私人的attr_reader :资源,:选项def 初始化(资源,选项)@resources = 资源@options = 选项结尾定义过滤器如果选项[:公司]@resources = resources.where(company: options[:company])结尾如果选项[:位置]@resources = resources.where(位置:选项[:位置])结尾资源结尾结尾

我使用 call 然后调用私有的 initializefilter 方法的原因是为了不错误地使用这个类.它有一个入口点并返回结果集.这也让我可以将过滤功能分解为易于更改的小块.

在这个例子中,我在 filter 中做了所有的过滤,但是你可以把它们分解成方法,甚至可以使用一些巧妙的元编程来调用过滤器名称,如果对象响应它.>

您也可以考虑寻找类似 ransack[1] 这样的 gem 解决方案,它允许您将参数直接传递给对象并进行搜索.有许多不同类型的搜索/过滤宝石可以满足您的需求.

[1] https://github.com/activerecord-hackery/ransack

I'm using Rails 5 API to build an app for retrieving data. I want to use multiple GET parameters to extract data.

Sample GET Request Format

/users?company=Samsung&position=Engineer

Controller Code for single parameter:-

def index
client = User.where("company = ?", params[:company])
render json: client
end

Controller Code for multiple parameters:-

def index
client = User.where("company = ?", params[:company]).where("position = ?", params[:position])
render json: client
end

Likewise there are many parameters which may/may not be included in the GET request.

What I want

If a user hasn't specified any parameter, he should be shown all the users. If only a few parameters have been specified, only those parameters should be searched.

What is happening

If a user specifies only position parameter in the GET request, Rails can't handle it & shows a blank json as there's no rule specified for it.

Please note that there are many parameters and it's not possible to write a rule for each and every combination of parameters.

解决方案

The basic issue with your solution is that it's forcing each where to execute in turn. This means that if the company parameter is nil you get an empty relation and all where statements after that will return an empty relation. You need to conditionally filter only if the parameters are provided.

What you're trying to do is best done inside a query/search or filter object (query takes parameters and returns a result set, filter takes a set already generated and filters it down). I like to define these in app/services/ although some people put them in app/queries.

There are some inconsistencies in your method however, an index endpoint should return a list of objects. Even if you're hoping to get back a single option you should expect that there could be multiple. Let's say we fix up your index endpoint and use a filter object.

# app/controllers/users_controller.rb
def index
  users = User.where(1=1)
  users = Users::Filter.call(users, params)
  render json: users
end

You'll notice I use User.where(1=1), this is because I'm use to dealing with rails 3 which returns an array instead of a relation if you use User.all.

# /app/services/users/filter.rb
class Users::Filter
  def self.call(resources, options)
    new(users, options).filter
  end

  private

  attr_reader :resources, :options

  def initialize(resources, options)
    @resources = resources
    @options   = options
  end

  def filter
    if options[:company]
      @resources = resources.where(company: options[:company])
    end

    if options[:position]
      @resources = resources.where(position: options[:position])
    end

    resources
  end
end

The reason I use call which then invokes the private initialize and filter method is so that this class isn't used incorrectly. It has a single point of entry and returns the result set. This also allows me to break out my filtering functions into small bite size chunks that are easy to change.

For this example I did all of the filtering in filter but you could break these out into methods and even use some clever meta programming to call the filter name if the object responds to it.

EDIT:

You may also consider finding a gem solution like ransack[1] which allows you to pass parameters directly to the object and search it. There are many different types of search/filtering gems out there to meet your needs.

[1] https://github.com/activerecord-hackery/ransack

这篇关于在 Rails 5 API 中管理多个 GET 参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆