How to use Rails Strong Parameters
06 Dec 2015In the latest major version of Ruby on Rails, Strong Parameters were introduced. The intent of this addition was to enable consistent and reliable parameter checking. Using Strong Parameters is simple and intuitive. It provides a very clean method API to help keep controllers DRY.
However, knowing when to use Strong Parameters and how to use them correctly is very important. After all, what good is parameter validation if it is in the wrong place or conveys the wrong message?
Hit the Books
The trusty “booksandreviews.com” will serve as a good example application.
Given a controller that can create Authors
, a default pattern without Strong Parameters could look like:
class AuthorsController < ApplicationController
# POST /authors
def create
@author = Author.create!(author_params)
redirect_to @author, notice: 'Author was successfully created.'
end
private
def author_params
params[:author]
end
end
Assuming that there are no presence validations on the Author
class itself (or in the form view creating this model), this code has no check to make sure that anything about an Author
is passed in to create.
This means that any POST
request is made to /authors
will create a new Author
. While this kind of problem would most likely not make it to production, let us assume that the poor people over at “booksandreviews.com” came from such humble code beginnings that this was the first iteration of the AuthorsController
.
To make the AuthorsController
more robust, the require
method can be used on the params
hash:
class AuthorsController < ApplicationController
# ...
private
def author_params
params.require(:author)
end
end
Now if a POST
to /authors
does not contain a payload with an :author
key in the body, the request will error:
ActionController::ParameterMissing
However, a request with the :author
attribute will…
ActiveModel::ForbiddenAttributesError
Also cause an error, apparently.
This is another feature of Strong Parameters. The reason an error occurs is that the author_params
method did not specify which parameters could be mass assigned to the Author
class. Passing the result of require
directly to Author.create
will trip this safety measure.
Shut ‘em Down
To correct the ForbiddenAttributesError
and add some much needed structure to the AuthorsController
, the permit
method needs to be used in conjunction with require
.
Adding a list of attributes as individual symbols will make sure that only those attributes can be passed through to Author.create!
class AuthorsController < ApplicationController
# ...
private
def author_params
params.require(:author).permit(:name, :country, :email)
end
end
Great! Now a properly formatted POST
containing only the correct attributes will create an Author
appropriately.
But, why go through all the hassle? There should be a way to let all attributes pass, regardless of how crazy harmful they could be to our application we use to pay bills.
The permit!
method is a completely permissive way to allow all attributes to be mass assigned to a model:
class AuthorsController < ApplicationController
# ...
private
def author_params
params.require(:author).permit!
end
end
This will allow any attributes nested under :author
to be set on an Author
. A solution like this requires no declaration of attributes, but is this better?
The short answer is “no”.
The reason why being completely permissive with parameters is a bad idea is because not all attributes were created equally.
If “booksandreviews.com” had an admin
or superuser
column on their authors
table, any POST
with admin: true
would create not a normal Author
, but an all-mighty Author
.
Specifying the attributes that can be mass assigned to an ActiveRecord
model is not a new concept, with Strong Parameters this pattern is just too easy not to do.
Open Up Shop
Another great feature of using Strong Parameters is type assertion. Regardless of ActiveRecord
’s type coercion, ensuring that data is as valid as early as possible is important.
Consider that an Author
can have an array of genres
which summarize their bodies of work. To ensure that this list of genres
is an array, a hash syntax can be used:
class AuthorsController < ApplicationController
# ...
private
def author_params
params.require(:author)
.permit(:name, :country, :email, { genres: [] })
end
end
By adding { genres: [] }
to the end of the permit
call, it ensures that the genres
value is always an array. This prevents random nonsense or a malformed request from compromising the Author
class’ data integrity. If something other than an array is passed as the genres
attribute, it will disregard it.
Alternatively, when a parameter is a hash (like preferences
), all the keys in the hash need to be present in the permit
call.
{
"preferences": {
"time_zone": "GMT",
"subscribed": true
}
}
class AuthorsController < ApplicationController
# ...
private
def author_params
params.require(:author)
.permit(:name,
:country,
:email,
{ preferences: [:time_zone, :subscribed] })
end
end
If this is a tad confusing, check out more examples at the official guide for Strong Parameters.
Everything in Moderation
Strong Parameters is not appropriate for every situation. In the case that a certain number of parameters must be present, or if one parameter must only be present when not accompanied by another specific parameter, Strong Parameters will fall short. The time and place to use Strong Parameters is when creating or updating an ActiveRecord
object.
Additionally, if the Ruby on Rails application in question was an API, Strong Parameters would only be effective in very specific circumstances. Since the feedback given by Strong Parameters is not as granular as an API would like to be, it could be hard to find many uses of it.