Node.js POST路由变量不更新:在线沙龙预订系统条件校验失效
Hey there! I see exactly what's tripping you up here—Node.js's asynchronous nature is making your validation checks run before your database queries finish, and that's why they're not working. Plus, the "Cannot set headers after they are sent" error happens because you're trying to send multiple responses (like redirects) when the async callbacks fire. Let's fix this step by step.
What's Wrong with the Original Code?
- Async Execution Order: Your
booking.find()calls are asynchronous, but theif (a === 0)/if (b === 0)checks run immediately after starting those queries. At that point,aandbare still their initial value of1, so the validation never triggers even when there's a conflict. - Multiple Response Attempts: If you tried adding
res.redirect()inside theforEachloops, you'd end up trying to send a response every time a matching booking is found—once the first redirect is sent, any subsequent attempts throw the "headers already sent" error.
Fixed Code Using Async/Await (Cleaner & Reliable)
Let's rewrite the POST route using async/await to handle the asynchronous database calls properly. This way, we wait for all queries to finish before running our validation logic.
var express = require("express"); var router = express.Router(); var salon = require("../models/salon"); var comment = require("../models/comment"); var booking = require("../models/booking"); var middleware = require("../middleware/index.js"); var flash = require("connect-flash"); router.get("/booking", middleware.canMakeBooking, function (req, res) { res.render("booking/new.ejs"); }); // Updated POST route with async/await router.post("/booking", middleware.canMakeBooking, async function (req, res) { try { const { salonname, date, time } = req.body; const bookingdate = new Date(date); const currdate = new Date(); // Normalize dates to compare only the date part (no time) const normalizedBookingDate = new Date(bookingdate.setHours(0, 0, 0, 0)); const normalizedCurrDate = new Date(currdate.setHours(0, 0, 0, 0)); // 1. Check if selected date is valid (today or future) if (normalizedBookingDate < normalizedCurrDate) { req.flash("error", "please select a valid date!"); return res.redirect("back"); } // 2. Find the salon by name const foundsalon = await salon.findOne({ name: salonname }); if (!foundsalon) { req.flash("error", "no such salon"); return res.redirect("back"); } // 3. Check for existing bookings: both user's and salon's on the target date const [userExistingBooking, salonExistingBooking] = await Promise.all([ // Check if user already has a booking on this date booking.findOne({ "client.id": req.user._id, date: { $gte: normalizedBookingDate, $lt: new Date(normalizedBookingDate.getTime() + 24 * 60 * 60 * 1000) } }), // Check if salon already has a booking on this date booking.findOne({ salonName: salonname, date: { $gte: normalizedBookingDate, $lt: new Date(normalizedBookingDate.getTime() + 24 * 60 * 60 * 1000) } }) ]); // 4. Validate conflicts if (userExistingBooking) { req.flash("error", "you have already made a booking for this day"); return res.redirect("/salons"); } if (salonExistingBooking) { req.flash("error", "a booking for this day is already made by someone at this salon"); return res.redirect("/salons"); } // 5. Create the new booking if no conflicts const newbooking = { salonName: salonname, date: date, client: { id: req.user._id, username: req.user.username }, madeon: Date.now() }; await booking.create(newbooking); req.flash("success", "booking done!"); res.redirect("/salons"); } catch (err) { console.error(err); req.flash("error", "Something went wrong with your booking."); res.redirect("/salons"); } }); router.delete("/booking/:id", middleware.canMakeBooking, async function (req, res) { try { await booking.findByIdAndRemove(req.params.id); req.flash("success", "booking deleted"); // Fixed typo: added flash type res.redirect("back"); } catch (err) { console.error(err); req.flash("error", "Failed to delete booking."); res.redirect("back"); } }); module.exports = router; // Don't forget to export the router!
Key Improvements:
- Async/Await: Makes the code linear and easy to follow, ensuring we wait for database queries to complete before moving on.
Promise.all: Runs both booking checks in parallel instead of sequentially, making the process faster.findOneInstead offind: Stops searching as soon as a conflicting booking is found, which is more efficient than fetching all bookings and looping through them.- Date Range Query: Uses MongoDB's
$gteand$ltto check for bookings within the target day, which is more reliable than manually comparing dates in code. - Proper Error Handling: Wraps everything in a
try/catchblock to handle unexpected database errors gracefully. - Fixed Typo: In the delete route, you had
req.flash("booking deleted")—missing the flash type (like "success"), which would cause issues.
This should resolve both the validation not working and the "headers already sent" error. Let me know if you run into any other issues!
内容的提问来源于stack exchange,提问作者JOHN

