在 react/rails 应用程序中,在我发出发布请求后,我收到 500 或 422 等错误

In react/rails app, after i make a post request i receive errors like 500 or 422

提问人:Freaked Out 提问时间:10/24/2023 最后编辑:Freaked Out 更新时间:10/26/2023 访问量:70

问:

当我从 ruby on rails 的前端发出请求时,相同的控制器代码有效,但是当我从 react 发出 post 请求时,它最终会出错。

编辑:目标是上传文件并将其存储在公共文件夹中,以便以后显示。

react 的代码如下:

const handleChange = (e) => {
    e.preventDefault()
    setGame({ ...game, [e.target.name]: e.target.value })
}

const handleFile = (e) => {
    setGame({ ...game, file: e.target.files[0] })
}

const handleSubmit = async (e) => {
    e.preventDefault()
    setLoading(true)

    const formData = new FormData()
    for (var key in game) {
        formData.append(`${key}`, game[key]);
        console.log(`${key}` + ": " + game[key])
    }

    axios.post(`${import.meta.env.VITE_API_URL}/api/v1/projects`, formData)
        .then(response => {
            console.log(response)
        })
        .catch(error => console.log(error));
}

控制器的代码如下:

module Api
module V1
    class ProjectsController < ApplicationController
        protect_from_forgery with: :null_session
        skip_before_action :verify_authenticity_token
        
        before_action :set_project, only: %i[ show iframe edit update destroy ]
        current_user = User.first

        def index
            @projects = Project.all
            render json: @projects
        end

        # GET /projects/new
        def new
            @project = Project.new
        end

        def iframe
            @html_content = @project.files.blobs.find_by(content_type: 'text/html')&.download
            render layout: false # To render the iframe view without the default layout
        end

        def create
            @project = current_user.projects.new(project_params)
            respond_to do |format|
            if @project.save
                # Create the image folder and move image files
                add_files_to_image_folder(@project) # Call the method here

                format.html { redirect_to @project, notice: "Project was successfully created." }
                format.json { render :show, status: :created, location: @project }
            else
                format.html { render :new, status: :unprocessable_entity }
                format.json { render json: @project.errors, status: :unprocessable_entity }
            end
            end
        end

        def show
            @project = Project.find(params[:id])
            render json: @project
        end

        # GET /projects/1/edit
        def edit
        end

        def update
            respond_to do |format|
            if @project.update(project_params)
                # Call the method to add image files to the folder
                add_files_to_image_folder(@project)

                format.html { redirect_to project_url(@project), notice: "Project was successfully updated." }
                format.json { render :show, status: :ok, location: @project }
            else
                format.html { render :edit, status: :unprocessable_entity }
                format.json { render json: @project.errors, status: :unprocessable_entity }
            end
            end
        end

        def destroy
            @project = Project.find(params[:id])
            @project.destroy
        end

        private
        

        # Use callbacks to share common setup or constraints between actions.
        def set_project
            if Project.exists?(params[:id])
                @project = Project.find(params[:id])
            else
                redirect_to projects_path, alert: 'Uh oh looks like the game u wanted to play MAGICALLY DISPAPPEARED :00'
            end
        end
        

        # Only allow a list of trusted parameters through.
        def project_params
            params.require(:project).permit(:title, :description, files: [])
        end


        def add_files_to_image_folder(project)
            base_folder_path = Rails.root.join('public/uploads', project.folder_name)
            FileUtils.mkdir_p(base_folder_path)
            
            project.files.each do |file|
                # Check the content type of the file
                case file.content_type
                    when 'image/jpeg', 'image/png', 'image/gif', 'text/css', 'text/javascript', 'application/javascript'
                        # Save the file to the main folder
                        file_path = File.join(base_folder_path, file.filename.to_s)
                    
                        # Save the file to the respective folder
                        File.open(file_path, 'wb') { |f| f.write(file.download) }
                    else
                        # Skip files with unknown content types or handle them accordingly
                    next
                end
            end
        end
    end
end

结束

其余的 CRUD 操作工作正常,期待编辑,它部分工作,我能够编辑标题和描述,但编辑的文件部分现在也可以工作。 请帮我解决这个问题

rails 日志: 在 2023-10-24 20:04:14 +0500 启动了 127.0.0.1 的 POST“/api/v1/projects” 通过 Api::V1::P rojectsController#create 作为 HTML 进行处理 参数:{“title”=>“新井字游戏”, “description”=>“井字游戏描述”, “file”=>#<ActionDispatch::Http::UploadedFile:0x00007f003cda5690 @tempfile=#Tempfile:/tmp/RackMultipart20231024-13148-nlwpw6.erb, @content_type=“application/octet-stream”, @original_filename=“tictactoe.html.erb”, @headers=“Content-Disposition: form-data; name=”file“; filename=”tictactoe.html.erb“\r\n内容类型: application/octet-stream\r\n”>} 在 7 毫秒内完成 500 个内部服务器错误(ActiveRecord:0.0 毫秒 |拨款:569)

NoMethodError(nil:NilClass 的未定义方法“projects”):

app/controllers/api/v1/projects_controller.rb:35:在“创建”中

更新日志:

ActionController::ParameterMissing (param is missing or the value is empty: project):

app/controllers/api/v1/projects_controller.rb:89:在创建中 在 127-10-26 17:18:05 +0500 开始为 127.0.0.1 开机自检“/api/v1/projects” 通过 Api::V1::P rojectsController#create 作为 HTML 进行处理 参数:{“title”=>“new temp title”, “description”=>“new temp description”, “file”=>#<ActionDispatch::Http::UploadedFile:0x00007fbbda156c90 @tempfile=#Tempfile:/tmp/RackMultipart20231026-3448-amq41q.erb, @content_type=“application/octet-stream”, @original_filename=“tictactoe.html.erb”, @headers=“Content-Disposition: form-data; name=”file“; filename=”tictactoe.html.erb“\r\n内容类型: application/octet-stream\r\n”>} 用户负载 (0.5ms) SELECT “users”.* FROM “users” ORDER BY “users”.”id“ ASC 限制 ?[[“限制”, 1]] ↳ app/controllers/api/v1/projects_controller.rb:11:在“set_user”中 在 29 毫秒内完成 400 个错误请求(ActiveRecord:7.7 毫秒 |拨款:1240)project_params' app/controllers/api/v1/projects_controller.rb:30:in

ActionController::P arameterMissing(缺少参数或值为空:project):

app/controllers/api/v1/projects_controller.rb:89:在创建中project_params' app/controllers/api/v1/projects_controller.rb:30:in

更新日志: 在 2023-10-26 18:27:34 +0500 开始为 127.0.0.1 开机自检“/api/v1/projects” 通过 Api::V1::P rojectsController#create 作为 HTML 进行处理 参数:{“title”=>“new title”, “description”=>“new description”, “file”=>#<ActionDispatch::Http::UploadedFile:0x00007fbbbb0437f0 @tempfile=#Tempfile:/tmp/RackMultipart20231026-3448-9efnt8.erb, @content_type=“application/octet-stream”, @original_filename=“tictactoe.html.erb”, @headers=“Content-Disposition: form-data; name=”file“; filename=”tictactoe.html.erb“\r\nContent-Type: application/octet-stream\r\n”>}

在 1 毫秒内完成 500 个内部服务器错误(ActiveRecord:0.0 毫秒 |拨款:447)

NoMethodError(nil:NilClass 的未定义方法“projects”):

app/controllers/api/v1/projects_controller.rb:31:在“创建”中

JavaScript Reactjs Ruby-on-Rails Axios CRUD

评论

0赞 zaphodbln_ 10/24/2023
你能发布传入请求的 Rails-Logs 吗?也许你可以比较来自本地和 react-environment 的这些日志。您是否在日志中发现任何“unpermitteld attributes”条目?
0赞 max 10/24/2023
你真的需要/想重新发明文件上传轮子吗?ActiveStorage 提供用于处理上传的服务器端和客户端组件
0赞 max 10/24/2023
如果你确实想重新发明轮子,你还有很多工作要做。您需要发送请求,并且必须将文件作为单独的部分添加到请求中。您还需要提供用于 CSRF 保护的令牌。在查看此代码时,我不明白的是,它看起来像您正在使用 ActiveStorage,但 add_files_to_image_folder 中存在所有这些您不需要的奇怪代码。multipart/formdata
0赞 Freaked Out 10/24/2023
@zaphodbln_我已经将数据添加到了 Rails,我想你在说 Rails 日志时指的是这一点
0赞 ztcollazo 10/24/2023
你能检查一下是什么吗?看起来它可能是零,所以我只会确保用户在控制器方法中登录,或者使其成为经过身份验证的仅限用户的路由。current_user

答:

0赞 zaphodbln_ 10/26/2023 #1

正如我们从错误消息中看到的,问题在于这一行:

@project = current_user.projects.new(project_params)

如消息所示,current_user为 null。将其用作局部变量,该变量在方法中不可用。

一个正确的解决方案是使其成为实例变量,并在 before 操作中设置它。例如,您可以执行以下操作:

class ProjectsController < ApplicationController
        protect_from_forgery with: :null_session
        skip_before_action :verify_authenticity_token
     
        before_action: set_user  
        before_action :set_project, only: %i[ show iframe edit update destroy ]
...

        def set_user
          @current_user = User.first  # notice the leading @
        end

改变

@project = current_user.projects.new(project_params)

@project = @current_user.projects.new(project_params)

你可以继续。


解决了最初的 500 错误后,让我们解决 ActionController::P arameterMissing 错误。您必须按照我的最新评论中的规定更改发布的数据。改变

const formData = new FormData()
for (var key in game) {
    formData.append(`${key}`, game[key]);
    console.log(`${key}` + ": " + game[key])
}

到这样的东西:

const formData = new FormData()  
let project_parameters = {}
for (var key in game) {
  project_parameters[key] = game[key]
  console.log(`${key}` + ": " + game[key])
}
formData.append(`project`, project_parameters)     // <<- wrap it all up in the project-param

评论

0赞 Freaked Out 10/26/2023
zaphodbln_,我不知道为什么“@”不起作用,但我已经尝试了您的建议,但现在它最终出错:400(错误请求)。你能看一下rails日志并确认我是否得到了正确的文件格式吗?
0赞 zaphodbln_ 10/26/2023
您是在谈论问题中的日志吗?添加 @ 是不够的,您还必须确保设置了 @current_user - 这就是我添加第二个 before_action 标签的原因(请不要在我的答案中添加第一个代码块)否则您将不得不发布新生成的日志(可能)另一个错误消息。
0赞 Freaked Out 10/26/2023
我也试过了,但它仍然以错误 400 错误请求告终,是的,我指的是问题中的日志
0赞 zaphodbln_ 10/26/2023
至少“NoMethodError (undefined method create'”)行应该已经消失 - 所以应该有不同的日志......projects' for nil:NilClass): app/controllers/api/v1/projects_controller.rb:35:in
0赞 zaphodbln_ 10/26/2023
另一个观察结果 - 与上述错误消息无关 - 您上传的文件将不会被存储: 日志:@content_type=“application/octet-stream”, @original_filename=“tictactoe.html.erb” 条件:当 'image/jpeg', 'image/png', 'image/gif', 'text/css', 'text/javascript', 'application/javascript' 时,您可能还想尝试不同的文件类型