在Rails中使用Postgres UUID时,请避免PG :: InvalidTextRepresentation错误

tyb*_*103 10 postgresql ruby-on-rails rails-postgresql ruby-on-rails-4 rails-activerecord

我开始使用Postgres UUID类型用于我所有模型的id字段.非常好用,并且在Rails 4中支持(大部分):

create_table :users, id: :uuid do |t|
  # ...
end
Run Code Online (Sandbox Code Playgroud)

问题是,如果您尝试查找id为X的行,但Postgres将引发错误,但X不是格式正确的UUID字符串.

> User.find "3ac093e2-3a5e-4744-b49f-117b032adc6c"
ActiveRecord::RecordNotFound # good, will cause a 404
> User.find "foobar"
PG::InvalidTextRepresentation: ERROR # bad, will cause a 500
Run Code Online (Sandbox Code Playgroud)

因此,如果我的用户位于URL中UUID所在的页面上,然后他们尝试更改UUID,则会得到500错误而不是404.或者他们可能会获得一个不再存在的对象的链接.

我怎样才能以干燥的方式避免这种情况?我不能只是拯救PG::InvalidTextRepresentation和渲染404,因为其他东西也可能导致这个错误.

UPDATE

我认为ID参数格式的正则表达式是干净的,如果它不匹配则会引发404:

resources :users, id: /uuid-regex-here/
Run Code Online (Sandbox Code Playgroud)

但我仍然有待保持干燥的问题; 我不想把它放在我的路线中的每一个资源上.我可以在一个语句中声明多个资源,但前提是不要像成员操作那样使用其他选项.所以也许更好的问题是:有没有办法为所有路由设置id正则表达式?

tyb*_*103 7

您可以通过一次向多个路由添加路由约束constraints() do ... end.

我最终这样做并在所有:id参数上设置全局约束以将其与UUID正则表达式匹配:

MyApp::Application.routes.draw do
  constraints(id: /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i) do

    # my routes here

  end
end
Run Code Online (Sandbox Code Playgroud)

这样,/ posts/123或/ posts/foobar在调用控制器操作之前不再匹配/ posts /:id和404,从而避免了PG类型错误.

我的所有模型都将使用UUID作为其ID,因此这是干净且干燥的.如果我有一些带有整数ID的模型,它会更不干净.