Форма регистрации 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

Другие вопросы по тегам