首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么我的生产日志显示ActiveRecord::RecordNotUnique错误,而我的本地binding.pry显示ActiveModel::Errors?

为什么我的生产日志显示ActiveRecord::RecordNotUnique错误,而我的本地binding.pry显示ActiveModel::Errors?
EN

Stack Overflow用户
提问于 2019-07-26 22:44:48
回答 2查看 194关注 0票数 1

我有一个注册表,其中包含对模型和数据库的验证,以防止重复的条目。

我在生产中使用蜜巴格来记录我的错误。当用户尝试使用相同的凭据注册时,honeybadger会报告一个ActiveRecord::RecordNotUnique: Mysql2::Error: Duplicate entry for ....。错误的其余部分包含PII,这是我试图防止的(我在一家金融公司工作,所以这是一个合规性问题)。

我的解决方案是将Model.create包装在一个救援块中,并在其报告之前自定义蜜腺癌错误。我为它写了一些rspecs,但总是失败。当我在块中包含一个binding.pry时,我可以看到复制创建错误,但该错误是ActiveModel::Errors的一个实例。现在,我可以尝试修复ActiveModel::Errors错误,但我担心ActiveRecord::RecordNotUnique错误仍然会记录在生产环境中,这也是我正在努力更改的。

我搞不懂:

1)为什么生产和本地会显示不同类型的错误?

2)为了从我们的生产日志记录中提供自定义错误消息(并隐藏PII),我必须避免哪种类型的错误。

任何帮助都将不胜感激。谢谢!

我尝试过的一些整体解决方案是:

1)使用预准备语句。但是,这不起作用,因为我使用的ActiveRecord (4.2.11)版本没有预准备语句。

2)使用Honeybadger的能力来忽略以下错误:https://docs.honeybadger.io/lib/ruby/getting-started/ignoring-errors.html,但是,团队决定我们不想完全关闭错误。

代码语言:javascript
复制
# /app/models/prime_signup.rb
class PrimeSignup < ActiveRecord::Base
 validates_presence_of :first_name, :last_name, :email
 validates :email, uniqueness: true

  def person
    @person ||= Person.find_by(email: email)
  end

  def full_name
    "#{first_name} #{last_name}"
  end
end
代码语言:javascript
复制
# /db/schema.rb
create_table "prime_signups", force: :cascade do |t|
    t.string   "first_name",   limit: 255
    t.string   "last_name",    limit: 255
    t.string   "email",        limit: 255
    t.string   "phone_number", limit: 20
  end

  add_index "prime_signups", ["email"], name: "index_prime_signups_on_email", unique: true, using: :btree
代码语言:javascript
复制
# /app/controllers/api/v1/prime_signups_controller.rb

class API::V1::PrimeSignupsController < API::V1Controller
 // omitting skip_before_actions for brevity

  def create
    return render_forward_compatible_json_error(json_error, resource) unless resource.valid?
    service.perform
    render json: resource, serializer: API::V1::PrimeSignupSerializer, status: 201
  end

  private

  def resource_params
    params.require(:prime_signup).permit(:first_name, :last_name, :email, :phone_number,
                                         :utm_source, :utm_medium, :utm_campaign, :utm_term,
                                         :utm_content)
  end

  def resource # This is the method I'm trying to rescue the error from
    binding.pry 
    begin
      @resource ||= PrimeSignup.create(resource_params)
    rescue ActiveRecord::RecordNotUnique => e  # This is how I'm trying to customize the error
      Honeybadger.notify(
        error,
        error_message: 'Duplicate Entry',
      )
    end
  end

  def json_error
    JSONExceptions::InvalidFieldValues.new(detail: resource_errors)
  end

  def resource_errors
    resource.errors.messages.map {|field, message| "#{field} #{message.join}."}.join(" ")
  end

  def service
    ::Services::PrimeSignupCreation.new(resource)
  end

  def render_forward_compatible_json_error(error, resource)
    json_error_format = { errors: [error.to_json] }
    resource_key = resource.class.name.snakecase
    old_error_format = {resource_key => resource.errors.details}
    render json: json_error_format.merge(old_error_format), status: error.status
  end
end
代码语言:javascript
复制
require 'rails_helper'

describe 'API::V1::PrimeSignups', type: :request do
  describe 'POST /api/v1/prime_signups' do
    context 'duplicate entry' do
      it 'raises a custom honeybadger error' do

        prime_signup_params = {
          prime_signup: {
            first_name: "Walter",
            last_name: "White",
            email: "walter@white.com",
            phone_number: '123456789'
          },
          authenticity_token: 'authenticated',
          format: :json
        }

        expect(Honeybadger).to receive(:notify)
        VCR.use_cassette('/api/v1/prime_signups') do
          post '/api/v1/prime_signups', prime_signup_params.to_json, { "CONTENT_TYPE"=>"application/json" }
          post '/api/v1/prime_signups', prime_signup_params.to_json, { "CONTENT_TYPE"=>"application/json" }
        end
        expect(response.status).to eq(422)

        # VCR.use_cassette('/api/v1/prime_signups') do
        #   post '/api/v1/prime_signups', prime_signup_params.to_json, { "CONTENT_TYPE"=>"application/json" }
        # end
      end
    end
  end
end

我希望从中救出的错误是ActiveRecord::RecordNotUnique类型,或者记录的生产错误是ActiveModel::Errors类型。基本上是希望两者之间保持一致。

此外,任何关于规范的指导和更好的格式它将非常感谢。我在这方面做得很糟糕。

EN

回答 2

Stack Overflow用户

发布于 2019-07-26 23:15:28

您在生产中看到ActiveRecord::RecordNotUnique,但在本地看到ActiveModel::Errors的原因可能是由于生产中的竞态条件,您没有在本地复制。

也就是说,在生产中可能发生的情况是,唯一性验证正在通过,因为试图同时创建两个具有重复信息的记录,并且每个正在运行的记录都是有效的,因为它们都在数据库中找不到具有相同电子邮件地址的现有记录。其中一次创建成功,然后第二次失败,因为第一次在第二次执行唯一性查询之后但在执行插入之前被数据库持久化。

在您的测试环境中,两次尝试的创建是顺序进行的,而不是同时进行的,因此第二次尝试不会通过唯一性检查。

要测试控制器的生产行为,您需要存根PrimeSignup.create并让它引发ActiveRecord::RecordNotUnique错误。

虽然你没有问这个,但我也会给你一个替代方法的建议,因为我恰好是Honeybadger的联合创始人之一……:)您可以在Honeybadger配置中将email参数添加到list of filtered parameters,然后该PII将不会与其余的错误信息一起报告。

票数 0
EN

Stack Overflow用户

发布于 2019-07-26 23:27:38

这样,如果验证失败,Rails验证应该不允许保存,而且您根本不需要处理救援异常。

代码语言:javascript
复制
def resource 
  @resource ||= PrimeSignup.find_or_initialize_by(resource_params)
    if @resource.save
      Honeybadger.notify(
        @resource.errors.full_messages,
        error_message:  @resource.errors.full_messages.join(', ')
      )
    end
  end
end
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/57222383

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档