Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Формы, CSRF и flash-сообщения

Соберём форму добавления статьи целиком: страницу с формой, защиту от поддельных запросов, вывод ошибок рядом с полями и сообщение «Статья опубликована» после сохранения. Всё это AdonisJS даёт из коробки в наборе Web.

Маршруты

// start/routes.ts
import router from '@adonisjs/core/services/router'
 
const PostsController = () => import('#controllers/posts_controller')
 
router.get('/posts/create', [PostsController, 'create']).as('posts.create')
router.post('/posts', [PostsController, 'store']).as('posts.store')

Первый маршрут показывает форму, второй принимает её данные.

Шаблон формы

{{-- resources/views/pages/posts/create.edge --}}
@layout({ title: 'Новая статья' })
  <h1>Новая статья</h1>
 
  <form method="POST" action="{{ route('posts.store') }}">
    {{ csrfField() }}
 
    <label>Заголовок
      <input type="text" name="title" value="{{ old('title') || '' }}">
    </label>
    @inputError('title')
      @each(message in $messages)
        <p class="error">{{ message }}</p>
      @end
    @end
 
    <label>Текст
      <textarea name="body">{{ old('body') || '' }}</textarea>
    </label>
    @inputError('body')
      @each(message in $messages)
        <p class="error">{{ message }}</p>
      @end
    @end
 
    <button type="submit">Опубликовать</button>
  </form>
@end

Метод create контроллера просто возвращает view.render('pages/posts/create'). Откройте /posts/create — появится форма.

Что здесь происходит

ЗаписьНазначение
csrfField()Скрытое поле с CSRF-токеном
old('title')Значение, которое пользователь ввёл до ошибки
@inputError('title')Блок выводится, если у поля есть ошибки; они лежат в $messages

CSRF (Cross-Site Request Forgery) — атака, при которой чужой сайт отправляет форму на ваш от имени вошедшего пользователя. Пакет Shield (config/shield.ts) отклоняет каждый POST без правильного токена.

Сохранение и flash-сообщение

// app/controllers/posts_controller.ts
async store({ request, response, session }: HttpContext) {
  const payload = await request.validateUsing(createPostValidator)
  const post = await Post.create(payload)
 
  session.flash('success', 'Статья опубликована')
  return response.redirect().toRoute('posts.show', { id: post.id })
}

Flash-сообщение хранится в сессии ровно до следующего запроса. Выведите его в макете:

{{-- resources/views/components/layout.edge --}}
@flashMessage('success')
  <div class="alert alert-success">{{ $message }}</div>
@end

Отправьте пустую форму — она вернётся с ошибками под полями и введёнными значениями. Отправьте правильную — откроется страница статьи с зелёной плашкой «Статья опубликована».

Формы для PUT и DELETE

HTML-формы умеют только GET и POST. Для изменения и удаления метод передают в адресе формы параметром _method:

{{-- resources/views/pages/posts/show.edge --}}
<form method="POST" action="{{ route('posts.destroy', { id: post.id }) }}?_method=DELETE">
  {{ csrfField() }}
  <button type="submit">Удалить статью</button>
</form>

Это работает, когда в config/app.ts в разделе http стоит allowMethodSpoofing: true.

Частая ошибка

Если забыть csrfField(), при отправке формы появится ошибка Invalid or expired CSRF token и данные не сохранятся. Добавляйте csrfField() в каждую форму с методом POST, включая формы удаления и выхода.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.