1

将 Ruby on Rails 与 capistrano 和 git 结合使用,我遇到了一个烦人的问题。

我有一个控制器“人”,其索引操作如下所示:

def index
  @people = Person.find( :conditions => params[:search] )
end

Person 表中有一个布尔值“is_admin”列。假设有些人是管理员,有些人不是管理员,那么对http://localhost:3000/people?search[is_admin]=true的 get 调用应该用一些用户填充@people ......这对我的本地人来说是正确的当我在开发模式下运行应用程序时..

但是....当我部署到我的服务器帐户 ( railsplayground ) 时,对http://mydomain.com/people?search[is_admin]=true的调用无法找到任何匹配的项目。但是,如果我将...?search[is_admin]=true更改为...?search[is_admin]=1响应将按预期返回管理员用户...

回到我的本地电脑上,使用“1”而不是“true”失败了。

底线是

Person.find( :all, :conditions => { :is_admin => 'true' } )

在我的开发环境中工作,并且

Person.find( :all, :conditions => { :is_admin => 1 } )

在我部署的环境中工作。

为什么是这样?我该如何解决?

理想情况下,我想放置如下链接:

link_to( "Administrators", {
  :controller => '/people',
  :action => :index,
  :search => { :is_admin => true }
})

并获取管理员列表:)。

可能值得注意的是,我的开发数据库是一个 sqlite3 文件,而生产是一个 mysql 数据库......

编辑:我理解对用户输入的反对意见,但在这种特殊情况下,这是一个非常非常小的威胁。此外,我当前的代码在我的开发电脑或我的生产帐户中都能完美运行,但不能同时在两者上运行。最简单的解决方案似乎改变了 sqlite3 保存和解释布尔值的方式,所以我将我的问题改为“如何改变 sqlite 保存布尔值的方式”......如果 sqlite 能完美地模仿 mysql 的行为,它将为我的发展服务需要完美...

4

3 回答 3

3

问题是您将布尔值作为字符串传递,最终行为取决于活动数据库。这里有更详细的解释。

当您读取params[:search]变量时,内容是一个字符串并且没有类型。这是因为查询字符串无法理解是否

params[:search][:is_admin] = "true"

实际上意味着

params[:search][:is_admin] = "true"
params[:search][:is_admin] = true

同样,当您通过 1 时,您最终会得到

params[:search][:is_admin] = "1"

这不同于

params[:search][:is_admin] = 1

当您将值传递给查询时,因为您没有传递布尔值,所以数据库适配器不会转换该值。您的最终查询结果类似于

SELECT * FROM `persons` WHERE `persons.is_admin` = 'true'

SQLite3 将布尔值存储为 t/f 字符串。true存储为't'false存储为'f'。我猜它也能理解真/假,它会自动翻译你的查询。相反,MySQL 只理解 0/1 和 true/false,它会失败。

当您切换行为时,通过1而不是传递true,您会导致 SQLite 失败,因为它无法将字符串转换"1"'t'. 相反,MySQL 可以而且确实可以。

在这两种情况下,你都做错了。你不应该传递一个字符串,而是一个布尔值。此外,您永远不应该相信用户输入。

我的建议是params[:search]在喂食前让你正常化Person.find

于 2010-02-06T19:39:53.120 回答
2

我认为使用 sqlite 和 mysql 之间的区别是您的问题的原因。

但是,允许直接从查询字符串传入有条件的活动记录对我来说似乎是个坏主意,并且可能会使您的应用程序容易受到 sql 注入攻击。

于 2010-02-06T19:39:47.133 回答
0

@Simone 的回答解释了为什么你会出现这种行为。要在 sqlite3 和 mysql 中获得正确的结果,您应该通过:

Person.find( :all, :conditions => { :is_admin => true } )

如果您只有一级参数(您有params[:search][:something]但没有更深的参数),那么您可以使用以下条件创建正确的哈希:

my_conditions = Hash.new
params[:search].each do |item, value|
  my_conditions[item.to_sym] = value == 'true' ? true : value == 'false' ? false : value
end

它将遍历所有搜索参数并将所有更改'true'true和。如果会有其他价值,它将保持原样。当然你可以在这里放任何你想要的逻辑。如果它变得比你可以用或重写它更复杂。'false'falseifswitch

比你可以:

Person.all(:conditions => my_conditions)
于 2010-02-06T20:51:32.940 回答