更新我使用Devise 1.4.9进行身份验证,当我尝试创建一个电子邮件地址已存在于数据库中的新用户时,我的Devise生成的用户模型似乎没有捕捉到数据库引发的异常。按照ciclon的建议(见下面的回复),我创建新用户的代码是…
class Api::RegistrationsController < Api::BaseController
respond_to :json
def create
user = User.new(params[:user])
user.ensure_authentication_token!
if user.valid?
user.save
render :json=> user.as_json(:auth_token=>user.authentication_token, :email=>user.email, :user_id=>user.id), :status=>201
return
else
warden.custom_failure!
render :json=> user.errors, :status=>422
end
end
end
Devise生成的迁移包括电子邮件属性的索引,包括唯一性验证。。。
add_index :users, :email, :unique => true
这是用户模型。。。
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :token_authenticatable
attr_accessible :email, :password, :password_confirmation, :remember_me, :authentication_token
validates_uniqueness_of :email
def ensure_authentication_token!
reset_authentication_token! if authentication_token.blank?
end
def as_json(options={})
super(:only => [:email, :authentication_token, :id])
end
end
如果指定的电子邮件地址已经存在于数据库中,我希望数据库在保存过程中引发错误,让Rails捕获它并执行其他代码块以返回422和问题描述。事实并非如此,相反,我得到了一个SQLException错误和一个崩溃。。。
ActiveRecord::StatementInvalid (SQLite3::ConstraintException: constraint failed: INSERT INTO "users" ("authentication_token", "created_at", "current_sign_in_at", "current_sign_in_ip", "email", "encrypted_password", "last_sign_in_at", "last_sign_in_ip", "remember_created_at", "reset_password_sent_at", "reset_password_token", "sign_in_count", "updated_at") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)):
app/models/user.rb:33:in `ensure_authentication_token!'
app/controllers/api/registrations_controller.rb:7:in `create'
如何让我的代码返回一个带有错误描述的422?
提前感谢你的智慧!
我又读了你的帖子,现在我看到那里发生了什么。您有一个数据库索引,而Rails并不知道这一点,所以当您创建@user.save时,数据库会抛出一个未在Rails端捕获的异常。
也就是说,你有两个选择:
1.)捕获Rails:中的异常
begin
user.save
render :json=> user.as_json(:auth_token=>user.authentication_token, :email=>user.email, :user_id=>user.id), :status=>201
rescue
warden.custom_failure!
render :json=> user.errors, :status=>422
end
2.)将验证添加到您的模型中,并检查用户是否有效?:
关于您的用户模型:
validates_uniqueness_of :email
在您的控制器上:
if user.valid?
user.save
render :json=> user.as_json(:auth_token=>user.authentication_token, :email=>user.email, :user_id=>user.id), :status=>201
return
else
warden.custom_failure!
render :json=> user.errors, :status=>422
end
我认为您可能需要将验证添加到您的用户模型中。
validates_uniqueness_of :email
我会将其封装在异常处理程序中。试试这样的东西:
class Api::RegistrationsController < Api::BaseController
respond_to :json
def create
user = User.new(params[:user])
status = nil
json = nil
begin
user.ensure_authentication_token!
user.save!
status = 201
json = {
:auth_token => user.authentication_token,
:email => user.email,
:user_id => user.id
}
rescue StandardError => e
case e
when ActiveRecord::StatementInvalid, ActiveRecord::RecordNotUnique
# ActiveRecord::StatementInvalid -> Raised by SQLite Adapter
# ActiveRecord::RecordNotUnique -> Raised by Postgres Adapter
user.errors[:email] = 'already taken'
end
warden.custom_failure!
status = 422
json = user.errors
end
render :json => json, :status => status
end
end