如何在不将 ID 作为隐藏字段传递的情况下在另一个控制器的上下文中使用 rails 部分表单

How to use a rails form partial in the context of another controller without passing the ID as a hidden field

我正在尝试将评论表单嵌入到 rails Post 显示视图中,我可以让它工作的唯一方法是在评论表单中传递这个隐藏字段:

<%= form.hidden_field :post_id, value: "#{params[:id]}" %>

这是我的 Post 表演动作:

def show
  @comment = Comment.new
end

这是评论创建操作:

def create
  @user = current_user
  @comment = @user.comments.build(comment_params)
end

我尝试将此添加到评论创建操作,但它仍然说缺少 Post ID:

def create
  @user = current_user
  @post = Post.find(params[:id])
  @comment = @user.comments.build(comment_params).merge(post_id: @post.id)
end

我还尝试将 @post = Post.find(params[:id]) 添加到 Post 显示操作,认为如果 rails 具有该变量,那么评论创建操作将可以访问 @post.id ).

唯一可行的方法是在评论表单中添加 post_id 作为隐藏字段,但这似乎很危险,因为恶意用户可以在浏览器中编辑 html。我不知道他们为什么要这样做只是为了更改应用评论的 Post,但这似乎仍然不是正确的方法。

我不想要“嵌套形式”,因为评论是通过 post 形式创建的。

它实际上只是 Post 展示页面上的一个单独的评论表单。我假设这在 Rails 中很常见,但很难找到“正确”的方法。

Rails 方法是声明一个 nested resource:

# config/routes.rb
resources :posts do
  resources :comments, only: [:create]
end

这将创建路由 POST /posts/:post_id/comments,它以 RESTful 的方式连接两个资源,与将 post id 放在请求正文中相比,这使得正在发生的事情非常透明.

# app/views/comments/_form.html.erb
<%= form_with(model: [@post, @comment], local: true) do %>
  # don't create a hidden input since the 
  # post id is passed through the URL
<% end %>
# app/views/posts/show.html.erb
<%= render partial: 'comments/form' %>
# app/views/comments/new.html.erb
<%= render partial: 'comments/form' %>
class CommentsController < ApplicationController
  before_action :set_post, only: [:create]

  # POST /posts/1/comments
  def create
    @comment = @post.comments.new(comment_params) do |c|
      c.user = current_user
    end
    if @comment.save
      redirect_to @post, success: 'Comment Created'
    else
      render :new
    end
  end

  private
  def set_post
    @post = Post.find(params[:post_id])
  end
end

The only thing that works is adding the post_id as a hidden field in the Comment form, but this seems dangerous because a malicious user could edit the html in the browser. I don't know why they would want to do this just to change the Post that the comment gets applied to, but it still seems not the right way to do this.

你的问题想错了。如果用户只能对某些 post 发表评论,您需要在您的服务器上强制执行授权(例如使用 Pundit 或 CanCanCan)。

真正糟糕的是在隐藏输入中传递当前用户 ID,因为它使恶意用户很容易以其他用户身份创建资源。

# This is how you get pwned
<%= form.hidden_field :user_id, value: current_user.id %>

您想依赖会话存储,因为它已加密且更难篡改。