Express 注册表单的邮件验证中间件:从内联校验到 MX 检查
注册表单看起来简单,真跑在生产就露馅:同一份邮件检查逻辑被复制粘贴到每个创建用户的路径——注册、邀请好友、管理员加人——六个月后有人新增 "invite teammate" 接口,整个检查就忘了,因为它没有任何集中强制的地方,只是每个人都要记住的"约定"。
第一步:把校验抽成中间件
Express 中间件是自然解法:定义一次,挂到需要的路由上。
// middleware/verifyEmail.js
function isValidFormat(email) {
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return regex.test(email);
}
async function verifyEmailMiddleware(req, res, next) {
const { email } = req.body;
if (!email || !isValidFormat(email)) {
return res.status(400).json({ error: 'Please provide a valid email address' });
}
next();
}
第二步:格式之外——MX 记录检查
只查格式拦不住 test@test.com 或根本不收邮件的域名。next() 之前做异步查询:
const dns = require('dns').promises;
async function verifyEmailMiddleware(req, res, next) {
const { email } = req.body;
if (!email || !isValidFormat(email)) return res.status(400).json({ error: '...' });
const domain = email.split('@')[1];
try {
const mxRecords = await dns.resolveMx(domain);
if (mxRecords.length === 0) {
return res.status(400).json({ error: 'This domain cannot receive email' });
}
} catch {
return res.status(400).json({ error: 'This domain cannot receive email' });
}
next();
}
第三步:超时与失败要优雅处理
DNS 查询可能因与用户邮箱无关的原因挂起或失败(网络抖动、DNS 服务器超时)。如果中间件把每个失败都当"非法邮箱",会拒绝真实用户——需要区分"域名确定不收信"与"查询暂时失败",前者拒绝,后者放行或降级。
实践建议
- 集中式中间件比内联校验可靠:新路由默认获得检查,不再依赖记忆;
- 格式校验 + MX 查询分层,先挡垃圾输入再挡假域名;
- DNS 失败不等于邮箱非法,超时/失败路径要单独处理,别误伤真实用户。
来源:Building an Email Verification Middleware for Express.js Signup Forms - DEV Community