使用 params.require 时,如何获得更具体的 400 验证错误而不是 500 错误?

How can I get a more specific 400 validation error instead of a 500 error when using params.require?

提问人:CodingNewbie 提问时间:12/17/2022 最后编辑:Keivan SoleimaniCodingNewbie 更新时间:12/19/2022 访问量:328

问:

我正在使用后端的 Rails 和前端的 React 完成一个全栈项目。我有一个注册表单,用于接受新用户的信息和头像,并将其传递给后端的用户控制器。但是,无论出于何种原因,如果表单为空或不完整,则会抛出 500 内部服务器错误,而不是信息量更大的 400 错误。

用户控制器是:

`` UsersController 类 < ApplicationController rescue_from ActiveRecord::RecordInvalid,替换为::render_unprocessable_entity_response

def 指数 用户 = User.all 渲染 json:用户 结束

def 创建 用户 = User.create(user_params) 会话[:user_id] = user.id 渲染 json: user, status: :created 结束

  

    private
    
      def user_params
        params.require(:user).permit(:first_name, :last_name, :email, :birthday, :username, :password, :password_confirmation, :avatar) 
      end
    
      def render_unprocessable_entity_response(invalid)
        render json: { errors: invalid.record.errors.full_messages }, status: :unprocessable_entity
      end
    
    end

React 中的注册表单(特别是我的 Submit 函数)

  function handleSubmit(event) {
    event.preventDefault();
    const data = new FormData();

    data.append("[user][first_name]", firstName);
    data.append("[user][last_name]", lastName);
    data.append("[user][email]", email);
    data.append("[user][username]", username);
    data.append("[user][password]", password);
    data.append("[user][password_confirmation]", passwordConfirmation);
    data.append("[user][avatar]", avatar);

    fetch("/signup", {
      method: "POST",
      body: data
    })
    .then((response) => {
      if (response.ok) {
        response.json().then((user) => onLogin(user));
      } else {
        response.json().then((errorData) => contextData.setErrors(errorData.errors));
      }
    })
  };

当我提交空表单时,我在控制台中得到的响应:

  Parameters: {"user"=>{"first_name"=>"", "last_name"=>"", "email"=>"", "username"=>"", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "avatar"=>""}}
Completed 500 Internal Server Error in 97ms (ActiveRecord: 49.6ms | Allocations: 9234)

当我删除“params.require(:user)”部分时,它给了我预期的验证错误,但由于 ActiveStorage 工作并且需要数据,我无法创建新用户。

reactjs ruby-on-rails 错误处理 参数 form-submit

评论

0赞 max 12/17/2022
使用来自 React 的多部分表单请求是将其与 ActiveStorage 集成的一种非常笨拙的方式。ActiveStorage 具有内置的直接上传功能,可让您在使用 JavaScript 创建记录之前上传附件。不要重新发明轮子。edgeguides.rubyonrails.org/......

答:

0赞 max 12/17/2022 #1

这个问题的前提有很多错误,所以请耐心等待。

首先,让我们看一下以下文档:ActionController::Parameters#require

确保参数存在。如果存在,则返回 参数,否则会引发一个 ActionController::P arameterMissing 错误。

Rails 将在框架级别上挽救此错误,但您可以使用 在每个控制器级别上挽救它。但请记住,目的不是验证。如果请求缺少正确的结构并且无论如何都不应该被处理,那么它只是为了尽早保释 - 因为经典的 Rails 应用程序默认将其参数嵌套在与资源同名的键中。rescue_from 'ActionController::ParameterMissing'require

在 API 应用程序中,您甚至不必遵循该模式 - 您也可以使用“平面参数”或 JSONAPI.org 等标准。

然后是使用:

rescue_from ActiveRecord::RecordInvalid, with: :render_unprocessable_entity_response

这不时出现,作为一种“聪明”的方式来干燥 API。在你的情况下,它甚至不会起作用,因为不提高 - 确实。User.createUser.create!

这也不是对异常的良好使用。对于应导致立即停止执行的异常事件,应使用异常。传递无效输入的用户在任何方面都不是例外 - 事实上,它完全是平凡的。

如果您想以一种好的方式干燥您的 API,请按照 Responders gem 的方式进行:

class UsersController < ApplicationController

  def create
    user = User.create(user_params)
    session[:user_id] = user.id 
    respond_with(user)
  end
end
class ApplicationController
  # ...
  private

  def respond_with(resource)
    if resource.valid?
      resource.new_record? ? render_created_response(resource) : render_updated_response(resource)
    else
      render_unprocessable_entity_response(resource)
    end
  end

  def render_created_response(resource)
    render json: resource, status: :created
  end

  def render_updated_response(resource)
    render json: resource, status: :ok
  end
end

这将创建一个可在子类中重写的方法的显式流。而不是由实际回调创建的隐式流。

class UsersController < ApplicationController
  private

  def render_updated_response
    # do something wild and craazy
  end
end

保留 的用途,用于不应预期创建记录会失败(例如在种子文件或非交互式上下文中)或导致事务内部回滚的上下文。create!