[英]Rollback not triggering in rails transaction when catching errors
我在 controller 操作中有一些 Rails 代码,其中有一个带有自定义错误的事务。
当事务中发生特殊错误时,我希望事务回滚并呈现一些 json 代码,但是我似乎无法使事务回滚(它总是提交)然后呈现一些 json。这是我的以下代码:
def controller_action
error_message = nil
begin
ActiveRecord::Base.transaction do
code_that_moifys_db_to_rollback
code_that_causes_custom_error
some_more_code_i_dont_want_to_run
end
rescue SomeCustomError::Error
error_message = 'this is an error message'
raise ActiveRecord::Rollback
end
rescue ActiveRecord::Rollback
# no rollback is getting triggered and data is now corrupt if json rendered
render :json => {error: true, message: error_message}
# however doing "raise 'xxxxx'" will cause a DB rollback in the transaction
end
我如何:
我想我可能误解了回滚是如何触发的,最奇怪的是如果我在第二次救援中提出它会触发事务回滚。
我知道我来不及回答这个问题,但有些人可能仍在寻找解决方案。 因此,让我们从问题开始,然后转向解决方案。
如果您赶时间,可以直接跳到解决方案部分。
目标:
To render error response(other than saving object) and rollback transaction.
问题上下文
在 Rails 中,如果您在事务中保存了 object 错误(object.errors.present?),那么它将进入救援块(并且您可以在救援块中处理引发的异常)并且您的代码将如下所示。
def controller_action
ActiveRecord::Base.transaction do
if object.save!
render_success_response('success message here')
else
render_error_response(object.errors)
end
rescue => e
render_error_response(e)
raise ActiveRecord::Rollback
end
我将在此处定义两种方法来呈现我将使用的错误和成功响应。
def render_error_response(errors)
render :json => {errors: errors}
end
def render_success_response(success_message)
render :json => {success_message: success_message}
end
现在在上面的代码中,你必须手动回滚事务,但是如果你想让 Rails 为你回滚事务,那么你可以像这样编写上面的代码
def controller_action
begin
ActiveRecord::Base.transaction do
if object.save!
render_success_response('success message here')
else
render_error_response(object.errors)
end
rescue => e
render_error_response(e)
end
end
在上面的代码中,rails 会自动为你回滚事务,并会以异常错误进入救援块。
问题
但是,如果您有一个错误数组来响应(调用)服务或其他内容,并且如果该数组中存在错误您想要呈现这些错误并回滚事务怎么办? (如您所知,这次 Rails 不会为您回滚事务,因为这些错误不会保存 object 错误)
Rails 7 以下版本的解决方案
def controller_action
begin
ActiveRecord::Base.transaction do
response = ResponseFromService
if response[:errors].blank?
render_success_response('success message here')
else
render_error_response(response[:errors])
raise ActiveRecord::Rollback
end
rescue => e
render_error_response(e)
end
end
Rails 7 的解决方案
在 rails 7 中,我们得到了一个新特性,它正是针对这个问题的,它使用return关键字,所以在 rails 7 中,代码看起来像这样
def controller_action
begin
ActiveRecord::Base.transaction do
response = ResponseFromService
if response[:errors].blank?
render_success_response('success message here')
else
return render_error_response(response[:errors])
end
rescue => e
render_error_response(e)
end
end
警告:请注意,return 关键字将静默(不引发异常)回滚事务,因此在使用它时要小心。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.