Rails JSON API 参数验证和错误响应

Rails JSON API parameter validation & error responses

我是 Rails 的新手,有几个关于验证参数和 returning 错误响应的问题。我想使用新的 Rails 5 API 模式创建 JSON API。

据我所知,Rails 建议使用 "strong parameters" 作为验证参数的基线。例如,如果我想创建一个需要 phone 号码或电子邮件的用户 class,我会在我的 UsersController 中从类似这样的内容开始。

def create
  @user = User.new(create_user_params)
end

def create_user_params
  params.require(:user).permit(:email, :phone)
end

现在如果我想要更复杂的东西,我可能会添加以下内容

def create
  arr_contains_at_least_one(params[:user], [:email, :phone])
  @user = User.new(create_user_params)
end

这引出了我的问题。

在出现默认 Rails 错误 (ActionController::ParameterMissing) 或自定义错误时,return "pretty" 错误响应的最佳方式是什么?我的意思是,如果我在浏览器中调用 API 端点,它将 return 可读 JSON 带有描述性消息。如果我 运行 我的服务器处于生产模式,并且未能在第一个示例中提供用户参数,rails returns:

An unhandled lowlevel error occurred. The application logs may have details.

这显然不好,尤其是当我希望向最终用户显示错误时。这里我假设强参数模式是为了安全和数据完整性,而不是用户体验。模型字段验证似乎也是如此。所以我做了以下调整。

def create
  return error_response("some error") unless arr_contains_at_least_one(params[:user], [:email, :phone])
  @user = User.new(create_user_params)
end

def error_response(msg, status = 400)
  render json: {"code":status, "message": msg}, :status => status 
end

这行得通,但现在我不得不手动编写参数检查(检查强制参数是否存在,并验证电子邮件地址等参数)及其相应的错误响应。如果我也在模型上使用 Rails 的内置字段验证,这似乎违反了 DRY 原则。此外,设计良好的错误处理模式需要相当多的自定义实现。

我是不是错过了一些 Rails 魔法,还是我走对了路?

编辑:看起来活动记录验证可以很容易地包含在 JSON 响应中(http://guides.rubyonrails.org/active_record_validations.html),但问题仍然存在以验证参数的存在。

"Rails way" 应该使用表单往返 - 所有验证都应该存在于模型中。控制器可能只有简单的验证,如安全和白名单,与业务逻辑无关。 "Model" 可能不仅是 ActiveRecord 本身,还可能是 FormObject https://robots.thoughtbot.com/activemodel-form-objects 之类的东西,尤其是当您的某些资源未一对一反映到数据库表时。

如果你打算只使用 API 模式,我建议给一个非常整洁的 API DSL 的机会 https://github.com/ruby-grape/grape#parameter-validation-and-coercion

我认为您混淆了 strong_parameters 与验证方法的使用。

Strong parameters provides an interface for protecting attributes from end-user assignment. This makes Action Controller parameters forbidden to be used in Active Model mass assignment until they have been whitelisted.

http://edgeapi.rubyonrails.org/classes/ActionController/StrongParameters.html

如果您将强参数中的参数列入白名单,则意味着您将能够将它们批量分配给您的 ActiveRecord 模型。或者更好的是,未列入白名单的属性将不会传递给您的模型。

它们实际上与验证本身无关。它们旨在保护您的模型免受未经许可的参数注入。

def create
  @user = User.new(create_user_params)
end

def create_user_params
  params.require(:user).permit(:email, :phone)
end

所以你的例子:

POST /users body={user: {email: 'some@email.com', phone: '1234', some_other: 'some other'}}

some_other 属性不会传递给您的 User.new

您正在寻找的是 activerecord 验证。在你的具体情况下,我会写这样的东西:

def create
  @user = User.new(create_user_params)
  if @user.save
    render json: @user
  else
    render @user.errors.full_messages.as_json, status: 400
  end
end

def create_user_params
  params.require(:user).permit(:email, :phone)
end

然后在您的模型中:

class User < ActiveRecord::Base
  validates_precense_of :some_other #this will cause the user not to save, end thus, report an errer message
end