我已经尝试让它工作大约 8 个小时了。我一生都无法找到一个脚本来完成这个任务。我尝试了很多不同的脚本,我快要疯了。请参阅附加的我的 server.js 文件,该文件本身完全可以正常工作,没有任何问题,但是它将我网站的最终用户发送到新页面(/send-email)以发送 POST 并显示提交确认/错误消息。我希望这一切都可以在不离开表单所在页面的情况下完成。试图让它发挥作用简直要了我的命。我尝试过浏览旧论坛,甚至尝试过使用 ChatGPT、Llama 等一段时间,但 AI 在 JS 方面比我显然更差,哈哈。如果可以的话请帮助我。我快失去理智了
服务器.js: (在 Node.js/NGinx 服务器上运行)
const express = require('express');
const nodemailer = require('nodemailer');
const path = require('path');
const { body, validationResult } = require('express-validator'); // **Added for validation**
require('dotenv').config(); // Load environment variables from .env file
const rateLimit = require('express-rate-limit'); // Added for rate limiting
const app = express();
const port = process.env.PORT; // Use environment variable
//see end-user IP rather than cloudfare IP when limiting form submissions by IP
app.set('trust proxy', true);
// Serve static files (like your HTML form)
app.use(express.static(path.join(\__dirname, 'public')));
// Middleware to parse form data
app.use(express.urlencoded({ extended: true }));
// Configure rate limiter
const formSubmitLimiter = rateLimit({
windowMs: 24 \* 60 \* 60 \* 1000, // 24 hours
max: 3, // Limit each IP to 3 requests per `windowMs`
standardHeaders: true, // draft-6: `RateLimit-*` headers; draft-7: combined `RateLimit` header
legacyHeaders: false, // Disable the `X-RateLimit-*` headers.
message: 'Too many form submissions from this device, please try again later.',
});
// Route to handle form submission
app.post('/send-email',
// Honeypot check
(req, res, next) => {
if (req.body.body2) {
return res.status(400).send('Form submission failed.');
}
next();
},
// Apply rate limiter to the route
formSubmitLimiter,
// validation middleware
[
body('subject').isLength({ min: 1 }).withMessage('Subject is required'),
body('body').isLength({ min: 1 }).withMessage('Message body is required')
],
async (req, res) => {
const errors = validationResult(req); //Check validation errors
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const { subject, body } = req.body;
// Configure nodemailer transport for SMTP service
let transporter = nodemailer.createTransport({
service: process.env.SMTP_SERVICE,
auth: {
user: process.env.EMAIL_USER, // Use environment variable
pass: process.env.EMAIL_PASS // Use environment variable
}
});
try {
// Send email
let info = await transporter.sendMail({
from: `"RoomToRoamStudios" <${process.env.EMAIL_USER}>`, // Use environment variable
to: process.env.RECIPIENT_EMAIL, // Use environment variable
subject: subject,
text: body,
});
console.log('Message sent: %s', info.messageId);
res.status(200).send('Email sent successfully!');
} catch (error) {
console.error('Error sending email:', error);
res.status(500).send('Error sending email');
}
});
// Start server
app.listen(port, () =\> {
console.log(`Server listening on http://localhost:${port}`);
});
HTML 表单:
<form id="contactForm" action="/send-email" method="POST" style="z-index: 200;">
<div>
<input autocomplete="off" type="text" placeholder="Subject" name="subject" required />
</div>
<div>
<textarea autocomplete="off" placeholder="Your message" name="body" required></textarea>
</div>
<div class="oh-no-hun" >
<input tabindex="-1" autocomplete="off" type="text" name="body2" />
</div>
<div>
<button type="submit">Send a message</button>
</div>
</form>
<div id="message" class="message"></div>
我的各种有问题的脚本之一:
<script>
document.getElementById('contactForm').addEventListener('submit', async function(event) {
event.preventDefault(); // Prevent the default form submission
const form = event.target;
const formData = new FormData(form);
const messageDiv = document.getElementById('message');
// Manually construct the request payload
const payload = {
subject: formData.get('subject'),
body: formData.get('body'),
};
try {
const response = await fetch(form.action, {
method: form.method,
headers: {
'Content-Type': 'application/json', // Specify JSON content type
},
body: JSON.stringify(payload), // Convert payload to JSON
});
// Ensure response is in JSON format and read it
const contentType = response.headers.get('content-type');
let responseJson = {};
if (contentType && contentType.includes('application/json')) {
try {
responseJson = await response.json();
} catch (jsonError) {
console.error('Failed to parse JSON:', jsonError);
messageDiv.innerHTML = `<p class="error">Failed to parse server response.</p>`;
return;
}
} else {
const responseText = await response.text(); // Read as text if not JSON
console.error('Unexpected response format:', responseText);
messageDiv.innerHTML = `<p class="error">Unexpected response format. Response: ${responseText}</p>`;
return;
}
if (response.ok) {
// Display success message
messageDiv.innerHTML = `<p class="success">${responseJson.message || 'Email sent successfully!'}</p>`;
} else {
// Handle and display validation errors
const errors = responseJson.errors || [{ msg: 'An unknown error occurred.' }];
messageDiv.innerHTML = `<p class="error">${errors.map(err => err.msg).join(', ')}</p>`;
}
} catch (error) {
messageDiv.innerHTML = `<p class="error">An unexpected error occurred: ${error.message}</p>`;
}
});
</script>
我真的只需要知道该往哪个方向走,从剧本开始。如果你能为我指明编写剧本的正确方向,我会爱你的。如果你写得这么有效,我会高兴死的。
我想,我尝试过ajax,但据我所知,我做得非常错误,或者我根本没有这样做,但我认为我在这样做。我真的不知道了。我好累啊
我面临的主要问题是消息是必需的/正文是必需的错误,即使在提交时填写了主题和正文,偶尔也会出现错误:JSON.parse:JSON的第1行第1列出现意外字符我找到了修复的数据(.send 与 .json),但总的来说,我真的只是想废弃整个脚本并以比我一直在尝试的更好的方向重新开始
* 我也试过
curl -X POST https://roomtoroamstudios.com/send-email -F "subject=Test Subject" -F "body=Test message"
看看这是否会迫使它注意到主题和正文中的文本,但会返回相同的错误:
{"errors":[{"type":"field","msg":"Subject is required","path":"subject","location":"body"},{"type":"field","msg":"Message body is required","path":"body","location":"body"}]
如果您想查看,可以在这里找到该网站:roomtoroamstudios.com/connect
表单的第三个框是蜜罐(测试时可见),因此如果您提交表单,请勿在该框中输入任何内容
我将速率限制的最大值提高到每 24 小时 10 次提交,这样您就可以测试几次,但现在使用的脚本已修复 json 错误,只是有一个主要错误“主题是必需的,消息正文”尽管提交时填写了主题和正文,但仍显示“是必需的”
一切看起来都很好,除了一件事:您正在发送 JSON。但是您将服务器配置为仅解析 x-www-form-urlencoded 格式。要么在客户端发送 urlencoded,要么在服务器上启用 JSON 解析。
(您的curl示例也不起作用,因为
-F
用于发送multipart/formdata
而不是application/x-www-form-urlencoded
,而且您也没有在服务器上启用多部分解析。)
也许您应该验证服务器上的内容类型标头,如果不是预期的格式,则返回正确的错误,然后您会更快地发现此类问题。在开发工具或调试代理(如 Fiddler)中将工作请求与损坏的请求进行比较也会有所帮助。