Reputation:
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
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
Reputation: 18572
You can enforce this in your controller with something like this:
@comment = @article.comments.find(params[:comment_id])
Upvotes: 1