表单与验证
在本章中,你首先将添加让已登录用户创建新帖子的能力。然后,你将用相同的模式让用户对已有帖子发表评论。在此过程中,你将接触 AdonisJS 的验证层,并学习如何使用针对不同资源的独立控制器来组织代码。
概述
到目前为止,在 DevShow 教程中,你构建了一个展示数据库中帖子的应用。但如何创建新帖子呢?这正是表单的用武之地。
处理表单涉及三个主要步骤:
- 显示一个表单来收集用户输入。
- 在服务端验证该输入,确保满足你的要求。
- 最后将已验证的数据保存到数据库。
AdonisJS 提供了 Edge 表单组件,它们会渲染标准的 HTML 表单元素并带有自动的 CSRF 防护,还提供了 VineJS 来定义验证规则。
添加帖子创建
让我们先添加让用户创建新帖子的能力。我们需要一个控制器方法来显示表单、将一切连接起来的路由,以及表单本身的模板。
-
添加控制器方法
首先,让我们向你的
PostsController添加一个create方法,用于渲染创建新帖子的表单。我们还会先搁置一个store方法,稍后再实现它来处理表单提交。app/controllers/posts_controller.tsimport type { HttpContext } from '@adonisjs/core/http' import Post from '#models/post' export default class PostsController { // ... 已有方法(index、show) /** * 显示创建新帖子的表单 */ async create({ view }: HttpContext) { return view.render('posts/create') } /** * 处理创建新帖子的表单提交 */ async store({}: HttpContext) { // 我们稍后实现它 } } -
注册路由
现在让我们把路由接好。我们需要两条:一条用于显示表单,另一条用于处理提交。两者都应仅对已登录用户开放。
Warning/posts/create路由必须定义在/posts/:id路由之前。start/routes.tsimport router from '@adonisjs/core/services/router' import { middleware } from '#start/kernel' import { controllers } from '#generated/controllers' router.get('/posts', [controllers.Posts, 'index']) router.get('/posts/create', [controllers.Posts, 'create']).use(middleware.auth()) router.post('/posts', [controllers.Posts, 'store']).use(middleware.auth()) router.get('/posts/:id', [controllers.Posts, 'show'])auth()中间件确保只有已登录用户才能访问这些路由。未通过身份验证的访客将被重定向到登录页。 -
创建表单模板
使用 Ace CLI 创建表单的模板。
node ace make:view posts/create这会创建
resources/views/posts/create.edge。打开它并添加以下表单。resources/views/posts/create.edge@layout() <div class="form-container"> <div> <h1> Share your creation </h1> <p> Share the URL and a short summary of your creation </p> </div> <div> @form({ route: 'posts.store', method: 'POST' }) <div> @field.root({ name: 'title' }) @!field.label({ text: 'Post title' }) @!input.control({ placeholder: 'Title of your creation' }) @!field.error() @end </div> <div> @field.root({ name: 'url' }) @!field.label({ text: 'URL' }) @!input.control({ type: 'url', placeholder: 'https://example.com/my-creation' }) @!field.error() @end </div> <div> @field.root({ name: 'summary' }) @!field.label({ text: 'Short summary' }) @!textarea.control({ rows: 4, placeholder: 'Briefly describe what you are sharing' }) @!field.error() @end </div> <div> @!button({ text: 'Publish', type: 'submit' }) </div> @end </div> </div> @end这些 Edge 表单组件是入门套件的一部分。它们会渲染标准的 HTML 元素,并带有实用的功能,如自动 CSRF 防护(通过
@form)和验证错误展示(通过@!field.error())。 -
创建验证器
在处理表单提交之前,我们需要定义验证规则。AdonisJS 使用 VineJS 进行验证 ,这是一个基于 schema 的验证库,让你可以为数据定义规则。
使用 Ace CLI 创建验证器。
node ace make:validator post这会创建
app/validators/post.ts。添加一个createPostValidator来验证帖子的创建。app/validators/post.tsimport vine from '@vinejs/vine' /** * 验证帖子的创建表单 */ export const createPostValidator = vine.create({ title: vine.string().minLength(3).maxLength(255), url: vine.string().url(), summary: vine.string().minLength(80).maxLength(500), })vine.create()方法从一个 schema 创建一个预编译的验证器。在内部,我们为每个字段定义其类型和规则。title字段必须是 3-255 个字符之间的字符串。url字段必须是字符串且格式为 URL。summary字段必须在 80-500 个字符之间。
-
实现 store 方法
现在让我们实现
store方法来验证数据、创建帖子,并重定向用户。app/controllers/posts_controller.tsimport type { HttpContext } from '@adonisjs/core/http' import Post from '#models/post' import { createPostValidator } from '#validators/post' export default class PostsController { // ... 已有方法 async store({ request, auth, response }: HttpContext) { const payload = await request.validateUsing(createPostValidator) await Post.create({ ...payload, userId: auth.user!.id, }) return response.redirect().toRoute('posts.index') } }- 当表单提交时,
request.validateUsing()会验证数据。 - 如果验证失败,用户会被自动重定向回原页面,相关字段旁会显示错误。
- 如果验证成功,我们创建帖子并使用
auth.user.id(通过 HTTP 上下文可用)将其与已登录用户关联,然后重定向到帖子索引页。
现在访问
/posts/create,填写表单并提交。你的新帖子应该会出现在帖子页面上!尝试提交无效数据(如过短的摘要或无效的 URL)来实地查看验证错误。 - 当表单提交时,
为帖子添加评论
既然你可以创建帖子了,让我们添加让用户发表评论的能力。我们将为评论创建一个独立的控制器。在 AdonisJS 中,每个资源对应一个控制器是推荐的做法。
-
创建评论验证器
让我们先为评论定义验证规则。
node ace make:validator comment由于评论只有一个 content 字段,验证很简单。
app/validators/comment.tsimport vine from '@vinejs/vine' /** * 验证评论的创建表单 */ export const createCommentValidator = vine.create({ content: vine.string().trim().minLength(1), }) -
创建 CommentsController
使用 Ace CLI 生成一个新的控制器。
node ace make:controller comments这会创建
app/controllers/comments_controller.ts。添加一个store方法来处理评论提交。app/controllers/comments_controller.tsimport type { HttpContext } from '@adonisjs/core/http' import Comment from '#models/comment' import { createCommentValidator } from '#validators/comment' export default class CommentsController { /** * 处理创建新评论的表单提交 */ async store({ request, auth, params, response }: HttpContext) { // 验证评论内容 const payload = await request.validateUsing(createCommentValidator) // 创建评论并将其与帖子和用户关联 await Comment.create({ ...payload, postId: params.id, userId: auth.user!.id, }) // 重定向回帖子页面 return response.redirect().back() } }我们使用
params.id从路由参数获取帖子 ID,并通过postId用它把评论关联到帖子。response.redirect().back()将用户送回帖子页面。 -
注册评论路由
添加一条用于创建评论的路由,同样受 auth 中间件保护。
start/routes.tsimport router from '@adonisjs/core/services/router' import { middleware } from '#start/kernel' import { controllers } from '#generated/controllers' router.on('/').render('pages/home').as('home') router.get('/posts', [controllers.Posts, 'index']) router.get('/posts/create', [controllers.Posts, 'create']).use(middleware.auth()) router.post('/posts', [controllers.Posts, 'store']).use(middleware.auth()) router.get('/posts/:id', [controllers.Posts, 'show']) router.post('/posts/:id/comments', [controllers.Comments, 'store']).use(middleware.auth()) -
添加评论表单
打开你的
resources/views/posts/show.edge模板并添加评论表单。resources/views/posts/show.edge@layout() {{-- ... 已有帖子展示代码 ... --}} <div class="post-comments"> <h2>Comments</h2> <div class="post-comment-form"> @form({ route: 'comments.store', routeParams: post, method: 'POST' }) <div> @field.root({ name: 'content' }) @!textarea.control({ rows: 3, placeholder: 'Share your thoughts...' }) @!field.error() @end </div> <div> @!button({ text: 'Post comment', type: 'submit' }) </div> @end </div> {{-- ... 已有评论列表 ... --}} </div> @endrouteParams: post将 post 对象传递给路由辅助函数,它会提取post.id来生成正确的 URL,如/posts/1/comments。现在登录后访问任意帖子页面并尝试发表评论。提交后,你将被重定向回页面,在列表中看到你的评论。
你学到了什么
你现在已为 DevShow 应用添加了完整的表单处理与验证。以下是你达成的成果:
- 使用 Edge 表单组件(
@form、@field.root、@input.control等)创建表单 - 使用 VineJS 验证器定义验证规则
- 在控制器中使用
request.validateUsing()验证表单提交 - 使用
auth()中间件保护路由,确保只有已登录用户才能创建内容 - 使用
auth.user!.id将帖子和评论与用户关联 - 通过为不同资源创建独立控制器(PostsController 和 CommentsController)来组织代码
- 使用
@!field.error()组件自动处理表单错误