Forms are how users change data, and strong parameters are Rails' guard against mass-assignment attacks.
form_with(model: @article) builds a form bound to a model. Rails picks the URL and method by convention: POST to /articles for a new record, PATCH to /articles/:id for an existing one. Inside the block, field helpers name inputs article[title] and pre-fill current values:
<%= form_with model: @article do |f| %>
<%= f.label :title %>
<%= f.text_field :title %>
<%= f.text_area :body %>
<%= f.submit %>
<% end %>Other helpers: f.select, f.check_box, f.date_field, f.email_field, f.file_field, f.collection_select. The form includes a hidden CSRF token, which Rails verifies on every non-GET request.
Display validation errors with @article.errors.full_messages, or per field with @article.errors[:title]. Invalid fields are wrapped in a field_with_errors div for styling.
The submitted data arrives nested: params[:article][:title]. Passing raw params to create or update would let an attacker set any attribute, such as admin: true, so Rails refuses unpermitted params. Strong parameters whitelist what's allowed. Rails 8 adds params.expect, which also checks the shape and returns a 400 for malformed input:
params.expect(article: [:title, :body, tag_ids: []])The older equivalent is params.require(:article).permit(:title, :body, tag_ids: []). Arrays and nested hashes must be declared explicitly.
Example
# app/controllers/articles_controller.rb
def new
@article = Article.new
end
def create
@article = Current.user.articles.new(article_params)
if @article.save
redirect_to @article, notice: "Published!"
else
render :new, status: :unprocessable_entity
end
end
private
def article_params
# Rails 8: whitelist and check the shape in one call
params.expect(article: [:title, :body, :category_id, tag_ids: []])
end
# Submitted: { article: { title: "Hi", body: "...", admin: "1" } }
# -> admin is filtered out; a non-hash article param -> 400 Bad Request
# app/views/articles/_form.html.erb (excerpt)
# <% if article.errors.any? %>
# <ul><% article.errors.full_messages.each do |m| %><li><%= m %></li><% end %></ul>
# <% end %>Remember
- form_with(model:) picks the URL, method and field values by convention
- Strong parameters whitelist attributes before mass assignment
- params.expect (Rails 8) checks shape too and returns 400 for bad input
Watch out for
- Forgetting to permit array params like tag_ids: [], so they are silently dropped.