Формы, 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, включая формы удаления и выхода.