Форма регистрации Rails 7 не показывает сообщения об ошибках
Я следую учебнику по рельсам , в форме регистрации, если отправлена неверная информация о пользователе, страница регистрации должна быть повторно отображена с сообщениями об ошибках, но на самом деле это не так. Кажется, что даже если страница регистрации отображается
"render 'new'"
, переданный ему @user пуст. Как это исправить?
Обратите внимание, что в руководстве используется Rails 6, но на самом деле я использую Rails 7.0.2.3 с Ruby 3.1.1. Не уверен, что это причина.
приложение/контроллеры/users_controller.rb
class UsersController < ApplicationController
def new
@user = User.new
end
def create
@user = User.new(user_params)
if @user.save
else
render 'new'
end
end
def show
@user = User.find(params[:id])
end
private
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end
end
приложение/просмотры/пользователи/new.html.erb
<% provide(:title, 'Sign up') %>
<h1>Sign up</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_with(model: @user, local: true) do |f| %>
<%= render 'shared/error_messages' %>
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
<%= f.submit "Create my account", class: "btn btn-primary" %>
<% end %>
</div>
</div>
app/views/shared/_error_messages.html.erb
<% if @user.errors.any? %>
<div id="error_explanation">
<div class="alert alert-danger">
The form contains <%= pluralize(@user.errors.count, "error") %>.
</div>
<ul>
<% @user.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
приложение/активы/таблицы стилей/custom.scss
@import "bootstrap-sprockets";
@import "bootstrap";
/* variables */
$gray-medium-light: #eaeaea;
/* mixins */
@mixin box_sizing {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
/* miscellaneous */
.debug_dump {
clear: both;
float: left;
width: 100%;
margin-top: 45px;
@include box_sizing;
}
/* universal */
body {
padding-top: 60px;
}
section {
overflow: auto;
}
textarea {
resize: vertical;
}
.center {
text-align: center;
h1 {
margin-bottom: 10px;
}
}
/* typography */
h1, h2, h3, h4, h5, h6 {
line-height: 1;
}
h1 {
font-size: 3em;
letter-spacing: -2px;
margin-bottom: 30px;
text-align: center;
}
h2 {
font-size: 1.2em;
letter-spacing: -1px;
margin-bottom: 30px;
text-align: center;
font-weight: normal;
color: $gray-light;
}
p {
font-size: 1.1em;
line-height: 1.7em;
}
/* header */
#logo {
float: left;
margin-right: 10px;
font-size: 1.7em;
color: white;
text-transform: uppercase;
letter-spacing: -1px;
padding-top: 9px;
font-weight: bold;
&:hover {
color: white;
text-decoration: none;
}
}
/* footer */
footer {
margin-top: 45px;
padding-top: 5px;
border-top: 1px solid $gray-medium-light;
color: $gray-light;
a {
color: $gray;
&:hover {
color: $gray-darker;
}
}
small {
float: left;
}
ul {
float: right;
list-style: none;
li {
float: left;
margin-left: 15px;
}
}
}
/* sidebar */
aside {
section.user_info {
margin-top: 20px;
}
section {
padding: 10px 0;
margin-top: 20px;
&:first-child {
border: 0;
padding-top: 0;
}
span {
display: block;
margin-bottom: 3px;
line-height: 1;
}
h1 {
font-size: 1.4em;
text-align: left;
letter-spacing: -1px;
margin-bottom: 3px;
margin-top: 0px;
}
}
}
.gravatar {
float: left;
margin-right: 10px;
}
.gravatar_edit {
margin-top: 15px;
}
/* forms */
input, textarea, select, .uneditable-input {
border: 1px solid #bbb;
width: 100%;
margin-bottom: 15px;
@include box_sizing;
}
input {
height: auto !important;
}
#error_explanation {
color: red;
ul {
color: red;
margin: 0 0 30px 0;
}
}
.field_with_errors {
@extend .has-error;
.form-control {
color: $state-danger-text;
}
}
Отладочная информация сервера rails при нажатии кнопки отправки формы с недопустимым
Started POST "/users" for ::1 at 2022-04-06 00:55:04
Processing by UsersController#create as TURBO_STREAM
Parameters: {"authenticity_token"=>"[FILTERED]", "user"=>{"name"=>"foo123", "email"=>"foo123@asdf", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "commit"=>"Create my account"}
TRANSACTION (0.0ms) begin transaction
↳ app/controllers/users_controller.rb:8:in `create'
User Exists? (0.1ms) SELECT 1 AS one FROM "users" WHERE "users"."email" = ? LIMIT ? [["email", "foo123@asdf"], ["LIMIT", 1]]
↳ app/controllers/users_controller.rb:8:in `create'
TRANSACTION (0.0ms) rollback transaction
↳ app/controllers/users_controller.rb:8:in `create'
Rendering layout layouts/application.html.erb
Rendering users/new.html.erb within layouts/application
Rendered shared/_error_messages.html.erb (Duration: 0.4ms | Allocations: 403)
Rendered users/new.html.erb within layouts/application (Duration: 1.9ms | Allocations: 1891)
Rendered layouts/_shim.html.erb (Duration: 0.0ms | Allocations: 15)
Rendered layouts/_header.html.erb (Duration: 0.1ms | Allocations: 78)
Rendered layouts/_footer.html.erb (Duration: 0.1ms | Allocations: 51)
Rendered layout layouts/application.html.erb (Duration: 9.5ms | Allocations: 8695)
Completed 200 OK in 213ms (Views: 9.8ms | ActiveRecord: 0.2ms | Allocations: 11628)
2 ответа
В рельсах 7 форм отправляются как
TURBO_STREAM
по умолчанию. После отправки формы Turbo ожидает перенаправления, если статус ответа не находится в диапазоне 400-599.
render 'new' # default status is 200
При коде состояния 200 Turbo показывает ошибку в консоли браузера и страница не перерисовывается.
Чтобы Turbo принимал отображаемый HTML, измените статус ответа. По умолчанию вроде
:unprocessable_entity
(код состояния 422)
render "new", status: :unprocessable_entity
https://turbo.hotwired.dev/handbook/drive#redirecting-after-a-form-submission
https://github.com/hotwired/turbo/commit/4670f2b57c5d0246dfc0f6d10ff7d9a52a63fdca
Обновление: примечание к «Content-Type». Это относится к отправке формы по умолчанию с турбо.
В этой настройке турбо ожидает ответа html с
Content-Type: text/html;
. @puerile отметил, что отсутствие расширения .html в ваших представлениях также нарушает ответ.
Rails использует расширение .html для установки типа содержимого ответа на
text/html
. Когда расширение опущено, для типа содержимого устанавливается значение
text/vnd.turbo-stream.html
потому что форма отправляется как TURBO_STREAM , так как наш ответ не имеет
<turbo-stream>
это неправильный тип контента.
>> Mime[:turbo_stream].to_str
=> "text/vnd.turbo-stream.html"
Если у нас есть представление
views/users/new.erb
, так не пойдет:
if @user.save
redirect_to @user
else
# NOTE: this will render `new.erb` and set
# `Content-Type: text/vnd.turbo-stream.html` header;
# turbo is not happy.
render :new, status: :unprocessable_entity
end
Чтобы исправить это, используйте метод:
respond_to do |format|
if @user.save
format.html { redirect_to @user }
else
# NOTE: this will render `new.erb` and set
# `Content-Type: text/html` header;
# turbo is happy.
format.html { render(:new, status: :unprocessable_entity) }
end
end
или установите тип контента вручную:
if @user.save
redirect_to @user
else
render :new, status: :unprocessable_entity, content_type: "text/html"
# NOTE: you can also set headers like this
headers["Content-Type"] = "text/html"
end
Одно предостережение с последней настройкой заключается в том, что макет также должен быть без расширения .html , иначе
render :new
отрендерит new.erb без лейаута и турбо снова не будет радовать. Это не проблема при использовании
respond_to
метод.
https://api.rubyonrails.org/classes/ActionController/MimeResponds.html#method-i-respond_to
Если вы посмотрите на логи, то увидите, что Rails получает AJAX-запрос в виде турбо-потока:
Processing by UsersController#create as TURBO_STREAM
Где следует читать:
Processing by UsersController#create as HTML
Чтобы отключить турбо, вам нужно установить
data-turbo="false"
атрибут формы:
<% provide(:title, 'Sign up') %>
<h1>Sign up</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_with(model: @user, data: { turbo: false }) do |f| %>
<%= render 'shared/error_messages' %>
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
<%= f.submit "Create my account", class: "btn btn-primary" %>
<% end %>
</div>
</div>
В
local: false
опция работает только со старой библиотекой javascript Rails UJS, которая использовалась по умолчанию до Rails 7. Вы также можете отключить Turbo по умолчанию с помощью:
import { Turbo } from "@hotwired/turbo-rails"
Turbo.session.drive = false
Видеть:
https://turbo.hotwired.dev/handbook/drive#disabling-turbo-drive-on-specific-links-or-forms