构建 DevShow —— 一个社区展示网站
在本教程中,你将构建 DevShow。DevShow 是一个小型的社区展示网站,用户可以在这里分享自己构建的作品。 每个用户都能创建账户、发布一条"展示条目"(一个项目、工具、实验,或任何他们引以为傲的东西),并浏览他人创建的条目。
概述
本教程采用动手实践的方式,从零到一构建一个真实的应用。你不会孤立地学习各个功能,而是能看到 AdonisJS 中的一切是如何协同工作的:路由、控制器、模型、验证、身份验证与模板引擎汇聚在一起,构成一个可运行的 Web 应用。
到本教程结束时,你将构建出:
- 帖子列表与详情页 - 展示所有帖子以及带评论的单篇帖子详情
- 帖子的创建与编辑 - 通过表单创建和更新帖子,并带验证
- 评论系统 - 允许用户对帖子发表评论
- 授权 - 确保用户只能编辑/删除自己的帖子与评论
- 导航与样式 - 带有页面间合理导航的精致 UI
身份验证系统(注册、登录、登出)已包含在入门套件中,且功能完整。
理解入门套件
我们以 AdonisJS Hypermedia 入门套件为起点,它已经内置了身份验证。让我们通过打开路由文件,看看手头有哪些可用资源。
import { middleware } from '#start/kernel'
import { controllers } from '#generated/controllers'
import router from '@adonisjs/core/services/router'
router.on('/').render('pages/home').as('home')
/**
* 注册和登录路由 - 仅对访客开放
*/
router
.group(() => {
router.get('signup', [controllers.NewAccount, 'create'])
router.post('signup', [controllers.NewAccount, 'store'])
router.get('login', [controllers.Session, 'create'])
router.post('login', [controllers.Session, 'store'])
})
.use(middleware.guest())
/**
* 登出路由 - 仅对已登录用户开放
*/
router
.group(() => {
router.post('logout', [controllers.Session, 'destroy'])
})
.use(middleware.auth())
入门套件为我们提供了用户注册、登录与登出的路由。注意 middleware.guest() 如何确保只有未登录用户才能访问注册/登录页,而 middleware.auth() 则保护登出路由。
在整个教程中,我们都会使用 auth 中间件来保护需要身份验证的路由。
控制器如何工作
让我们看看注册控制器,了解请求是如何在应用中流转的。
import User from '#models/user'
import { signupValidator } from '#validators/user'
import type { HttpContext } from '@adonisjs/core/http'
export default class NewAccountController {
async create({ view }: HttpContext) {
return view.render('pages/auth/signup')
}
async store({ request, response, auth }: HttpContext) {
/**
* 验证提交的数据
*/
const payload = await request.validateUsing(signupValidator)
/**
* 在数据库中创建新用户
*/
const user = await User.create(payload)
/**
* 自动登录
*/
await auth.use('web').login(user)
/**
* 重定向到首页
*/
response.redirect().toRoute('home')
}
}
每个控制器方法都以 HTTP 上下文对象作为第一个参数。该上下文包含了当前请求的一切信息:请求数据、响应对象、认证状态、视图渲染器等。我们只需解构出需要的属性(view 用于渲染模板,request 用于表单数据,response 用于重定向,auth 用于身份验证)。
create 方法只是展示注册表单。store 方法则承担了主要工作:它验证数据、创建用户、登录并重定向到首页。这种将验证器、模型与认证汇集在一起的模式,将会贯穿整个教程。
你可能会注意到控制器引用了 User 模型和一个 signupValidator。入门套件已经包含了它们。我们将在
数据库与模型
章节了解模型如何工作,在
表单与验证
章节了解验证器。
视图如何工作
当控制器调用 view.render('pages/auth/signup') 时,AdonisJS 会查找一个模板文件并将其渲染为 HTML。让我们看看这个注册视图长什么样。
@layout()
<div class="form-container">
<div>
<h1> Signup </h1>
<p>
Enter your details below to create your account
</p>
</div>
<div>
@form({ route: 'new_account.store', method: 'POST' })
<div>
@field.root({ name: 'fullName' })
@!field.label({ text: 'Full name' })
@!input.control()
@!field.error()
@end
</div>
<div>
@field.root({ name: 'email' })
@!field.label({ text: 'Email' })
@!input.control({ type: 'email', autocomplete: 'email' })
@!field.error()
@end
</div>
<div>
@field.root({ name: 'password' })
@!field.label({ text: 'Password' })
@!input.control({ type: 'password', autocomplete: 'new-password' })
@!field.error()
@end
</div>
<div>
@field.root({ name: 'passwordConfirmation' })
@!field.label({ text: 'Confirm password' })
@!input.control({ type: 'password', autocomplete: 'new-password' })
@!field.error()
@end
</div>
<div>
@!button({ text: 'Sign up', type: 'submit' })
</div>
@end
</div>
</div>
@end
视图位于 resources/views 目录中。AdonisJS 使用 Edge 作为模板引擎。Edge 模板看起来与 HTML 相似,但带有以 @ 开头的特殊标签。
@layout() 标签用通用布局(页头、页脚、CSS)包裹页面内容。@form() 和 @field.root() 标签是入门套件自带的组件。它们会渲染标准的 HTML 表单元素,并内置 CSRF 防护与验证错误展示等功能。
当你访问 /signup 时,路由调用控制器的 create 方法,该方法渲染此视图,Edge 将其转换为浏览器显示的 HTML。
试着创建一个账户
在继续之前,先用 node ace serve --hmr 启动你的开发服务器,并尝试创建一个账户。先熟悉一下入门套件的工作方式,我们将在此基础上继续构建。