user4932805
user4932805

Reputation:

Rails URL Behavior

I have two models: Article and Comment.

An Article has_many Comments.

When I visit comments#show, articles/1/comments/2, I am able to access comments that do not belong to the article.

For example, in this case, comment with id 2 does not belong to article with id 1, yet I am still able to access this URL.

Any idea why?

Upvotes: 0

Views: 53

Answers (2)

Mohamad
Mohamad

Reputation: 35349

When you nest URLs, you are making an inference that the child resource belongs to the parent resource, and should be scoped to it.

Unless you enforce this scoping, you might as well not use nested URLs. Nesting the URL makes the article_id available in the params of the comments controller.

Say you wanted to show a single comment.

def CommentsController < ActionController::Base
  def show
    @article = Article.find(params[:article_id])
    @comment = @article.comments.find(params[:id])
  end
end

In response to your comment below, you generally want this to fail. In production, a missing resource responds with a 404 response code (page not found).

If you do want manual error handling, you can do something like this:

def CommentsController < ActionController::Base
  before_action :set_article_or_redirect_to_root

  def show
    @comment = @article.comments.find(params[:id])
  end

private

  def set_article_or_redirect_to_root
    @article = Article.find_by(params[:article_id])
    redirect_to root_path unless @article
  end
end

Notice I am using find_by, which does not raise an error if the article resource isn't found (as opposed to find).

Otherwise, if you want to use find, you need to rescue the resulting ActiveRecord::RecordNotFound error (for example, maybe you want to record the error in the logs).

  def set_article_or_redirect_to_root
    @article = Article.find(params[:article_id])
  rescue ActiveRecord::RecordNotFound
    redirect_to root_path
  end

Upvotes: 2

Teddy
Teddy

Reputation: 18572

You can enforce this in your controller with something like this:

@comment = @article.comments.find(params[:comment_id])

Upvotes: 1

Related Questions