我最近完成了一个项目,要求我将模型架构中生成的验证错误发送给用户。当用户的输入不符合设置以保存此类数据的规则时,就会生成验证错误。例如,如果我在注册时将密码设置为不超过6个字符,则当用户试图设置密码小于6个字符时会丢弃验证错误。
上图总结了我们在本文中要实现的目标。我们将在此页面上工作以实现这一目标。与整个项目的链接是here,在发布本文时仍在进行中。
让我们开始
先决条件
要遵循,您将需要对以下内容的基本理解:
由于我们将尝试将错误从服务器呈现到我们的视图,因此我们必须对Express.js有基本的了解。
设置模型发送错误
Mongoose允许我们通过定义某些属性来验证用户发送的数据类型来设计我们的架构。
NPM安装猫鼬
const mongoose = require("mongoose")
const { Schema } = mongoose
const { isEmail } = require("validator")
const AuthSchema = new Schema({
email: {
type: String,
required: [true, "Please enter an email"],
unique: true,
lowercase: true,
validate: [isEmail, "please enter a valid email"]
},
firstName: {
type: String,
required: [true, "Please enter your first name"],
capitalize: true
},
lastName: {
type: String,
required: [true, "Please enter your last name"],
capitalize: true
},
password: {
type: String,
required: [true, "Please enter password"],
minlength: [6, "Minimum password length is 6 characters"]
}
})
以上述代码中的电子邮件字段为例,它具有几个具有设置值的属性,但我们将查看required
和值在数组中的validate
属性。数组的第二个元素代表错误消息。
属性 | 错误消息返回 | 什么触发错误消息 |
---|---|---|
必需 | 请输入电子邮件 | 当电子邮件字段空白 | 时
veration | 请输入有效的电子邮件 | 当提供无效的电子邮件 | 时
处理来自猫鼬的错误
在将错误传递给控制器之前,我们需要具有修改错误的函数,以使其更易于使用线路。
const handleErrors = (err) => {
let errors = { email: "", firstName: "", lastName: "", password: "" }
// duplicate error
if (err.code === 11000) {
errors.email = "the email is already in use"
return errors
}
//validation errors
if (err.message.includes("auth validation failed")) {
// destructuring inside the forEach, similar to (err) => {err.properties}
Object.values(err.errors).forEach(({ properties }) => {
errors[properties.path] = properties.message
})
}
return errors
}
module.exports = handleErrors
上面的函数处理从猫绿色返回的长错误消息,确切了我们需要的一切,并将它们转换为更可读的对象。
将错误发送到视图
收到错误消息后,注册控制器会向视图发送错误消息(应该有一条)。让我们谈谈注册控制器,但在此之前,让我们看一下这些概念:
-
会话:从客户端发送请求时,会生成并保存在服务器端。该会话具有一个唯一的ID,该ID以及其保留的任何数据都发送到客户端,以存储在Cookie中。然后将此cookie随后的每个请求发送到服务器。此过程是使用express-session中间件软件包完成的。这个video很好地说明了这个概念。
-
当地人:
res.locals
属性是一个包含响应 - 局部变量的对象,仅在该请求/响应周期中呈现的视图可用。这意味着附加到res.locals
对象的任何变量将无法用于其他请求或视图。这是将数据从服务器端传递到客户端以在视图中使用的方式。这个概念通常用于模板引擎(例如EJS和Jade),以在客户端呈现动态内容。
谈到了这两个概念,让我们看看它们如何帮助将错误消息发送到视图
NPM安装Express Express-Session
const express = require("express")
const session = require("express-session")
const app = express()
app.use(
session({
secret: process.env.SESSION_SECRET,
saveUninitialized: true,
resave: false
})
)
app.use((req, res, next) => {
res.locals.content = req.session.content
delete req.session.content
next()
})
// set teplate engine as EJS
app.set("view engine", "ejs")
express-session
中间件使我们访问了req.session
。 req.session
对象使我们可以访问会话数据以及在其上设置新属性的能力。在您提供的示例中,req.session
用于设置content
属性,然后将其传递到res.locals.content
属性以供渲染视图。将req.session.content
分配给res.locals.content
后将删除。
重要的是要注意,res.lecals仅适用于该请求/响应周期中呈现的视图。
现在让我们谈谈注册控制器。
module.exports.signupPost = async (req, res) => {
const { email, firstName, lastName, password } = req.body
try {
const user = await User.create({ email, firstName, lastName, password })
res.status(200).json(user)
} catch (err) {
const errors = handleError(err)
req.session.content = {
email: email,
firstName: firstName,
lastName: lastName
}
res.render("pages/signup", {
title: "Signup Page",
error: errors,
content: req.session.content
})
}
}
上面的代码只是整个project的摘要。让我们专注于catch
块,因为那是我们的错误将被捕获并传递到视图的地方。
req.session.content
从上面的解释中分配了存储在其中的数据,即email
,first_name
和last_name
。这使我们能够像本文开头一样回到以前输入的字段以及我们的错误消息。
res.render
采用两个参数,第一个参数是要渲染的EJS文件的路径,而第二个参数则采用包含用于动态运行页面的数据的所有变量。
这是通过上述错误处理函数(handleError
)处理后渲染的错误。
风景
该视图结合了我们对HTML,Bootstrap和EJS的理解,这是理解本文的先决条件。
<%- include("layout/authheader") %>
<div class="container">
<div class="row">
<div class="col-lg-6 mx-auto mt-4">
<div class="card shadow">
<div class="card-header bg-secondary">
<h3 class="text-light">Sign Up</h3>
</div>
<div class="card-body p-4">
<form action="/signup" method="post" id="add-form" enctype="application/x-www-form-urlencoded">
<div class="mb-3">
<label for="email" class="form-label">Email</label>
<input type="email" name="email" class="form-control form-control-lg <%= error.email ? 'is-invalid' : '' %> " value="<%= content ? content.email.toString() : '' %>" placeholder="Enter email" required />
<% if (error.email !== "") { %>
<div class="invalid-feedback">
<%= error.email %>
</div>
<% } %>
</div>
<div class="mb-3">
<label for="firstName" class="form-label">First Name</label>
<input type="text" name="firstName" class="form-control form-control-lg <%= error.firstName ? 'is-invalid' : '' %> " value="<%= content ? content.firstName.toString() : '' %>" placeholder="Enter first name" required />
<% if (error.firstName !== "") { %>
<div class="invalid-feedback">
<%= error.firstName %>
</div>
<% } %>
</div>
<div class="mb-3">
<label for="lastName" class="form-label">Last Name</label>
<input type="text" name="lastName" class="form-control form-control-lg <%= error.lastName ? 'is-invalid' : '' %> " value="<%= content ? content.lastName.toString() : '' %>" placeholder="Enter last name" required />
<% if (error.lastName !== "") { %>
<div class="invalid-feedback">
<%= error.lastName %>
</div>
<% } %>
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input type="password" name="password" class="form-control form-control-lg <%= error.password ? 'is-invalid' : '' %> " placeholder="Enter password" required />
<% if (error.password !== "") { %>
<div class="invalid-feedback">
<%= error.password %>
</div>
<% } %>
</div>
<div class="mb-3 d-grid">
<input type="submit" name="submit" value="Sign up" class="btn btn-secondary btn-lg" />
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<%- include("layout/footer") %>
上面的代码显示了一个简单的HTML表单,该表格带有bootstrap。 Boostrap链接和脚本分别包含在layout/authheader
和layout/footer
中。可以在source code项目中检查一下。
<div class="mb-3">
<label for="email" class="form-label">Email</label>
<input type="email" name="email" class="form-control form-control-lg <%= error.email ? 'is-invalid' : '' %> " value="<%= content ? content.email.toString() : '' %>" placeholder="Enter email" required />
<% if (error.email !== "") { %>
<div class="invalid-feedback">
<%= error.email %>
</div>
<% } %>
</div>
以电子邮件输入字段为例,如果有任何错误,则使用三元运算符<%= error.email ? 'is-invalid' : '' %>
来更改框的边框颜色。
发送帖子请求时,此代码首先检查是否有电子邮件字段的错误消息(如果存在错误。
要快速注意的另一件事是,当没有错误消息时,EJ会引起错误,因此为了避免这种错误,我们必须将同一页面(当发送到页面上发送Get请求时),而错误集作为一个空字符串如下所示。
module.exports.signupGet = (req, res) => {
res.render("pages/signup", {
title: "SignUp Page",
error: "",
content: ""
})
}
就是这样!现在,您知道如何将验证错误从猫鼬模型呈现到您的观点。您还可以通过修改句柄功能以及将错误传递给视图的方式来自定义此过程以适应您的特定需求。