如何在MongoDB中存储URL并在Node.js网站代码中实现重定向与用户专属PDF下载功能
Hey there! Let's break down how to solve both of these requirements step by step using your MongoDB + Node.js + HTML/CSS stack. I'll cover database design, backend logic, and frontend implementation for each need.
1. MongoDB 数据模型设计
First off, we'll create a collection to store the external URLs. Let's call it redirectLinks—using Mongoose (the most common ODM for Node.js + MongoDB) to define the schema makes this straightforward:
// models/RedirectLink.js const mongoose = require('mongoose'); const redirectLinkSchema = new mongoose.Schema({ originalUrl: { type: String, required: true, trim: true, validate: { validator: (value) => { // Basic URL validation to ensure it starts with http/https return /^(https?:\/\/)/.test(value); }, message: 'Please enter a valid external URL starting with http:// or https://' } }, slug: { type: String, unique: true, trim: true, default: () => Math.random().toString(36).substring(2, 8) // Generate random short identifier }, createdAt: { type: Date, default: Date.now } }); module.exports = mongoose.model('RedirectLink', redirectLinkSchema);
The slug field gives you a clean, user-friendly path for redirects (like yourdomain.com/go/abc123) instead of using the MongoDB _id directly.
2. Node.js 后端实现 (Express)
Next, build two core endpoints: one to save the external URL to the database, and another to handle the actual redirect.
2.1 保存URL的接口
// routes/redirectRoutes.js const express = require('express'); const router = express.Router(); const RedirectLink = require('../models/RedirectLink'); // Save a new external URL and return the redirect path router.post('/save-url', async (req, res) => { try { const { originalUrl } = req.body; const link = new RedirectLink({ originalUrl }); await link.save(); res.status(201).json({ success: true, redirectPath: `/go/${link.slug}` }); } catch (err) { res.status(400).json({ success: false, message: err.message }); } });
2.2 重定向接口
// Handle the redirect logic router.get('/go/:slug', async (req, res) => { try { const { slug } = req.params; const link = await RedirectLink.findOne({ slug }); if (!link) { return res.status(404).send('Redirect link not found'); } // Use 302 for temporary redirects, 301 for permanent ones res.redirect(302, link.originalUrl); } catch (err) { res.status(500).send('Server error'); } }); module.exports = router;
Don't forget to register this router in your main Express app:
// app.js const redirectRoutes = require('./routes/redirectRoutes'); app.use('/', redirectRoutes);
3. 前端实现
Create a simple form to submit URLs and display the generated redirect link:
<!-- index.html --> <!DOCTYPE html> <html> <head> <title>URL Redirect Manager</title> <style> .container { max-width: 600px; margin: 2rem auto; padding: 0 1rem; } input, button { padding: 0.8rem; margin: 0.5rem 0; width: 100%; box-sizing: border-box; } .redirect-link { margin-top: 1rem; padding: 1rem; background: #f0f0f0; border-radius: 4px; } </style> </head> <body> <div class="container"> <h2>Save External URL for Redirect</h2> <form id="urlForm"> <input type="url" id="originalUrl" placeholder="Enter external URL (http://...)" required> <button type="submit">Save & Generate Redirect Link</button> </form> <div id="result" class="redirect-link" style="display: none;"></div> </div> <script> const form = document.getElementById('urlForm'); const resultDiv = document.getElementById('result'); form.addEventListener('submit', async (e) => { e.preventDefault(); const originalUrl = document.getElementById('originalUrl').value; try { const res = await fetch('/save-url', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ originalUrl }) }); const data = await res.json(); if (data.success) { resultDiv.style.display = 'block'; resultDiv.innerHTML = `Redirect link: <a href="${data.redirectPath}" target="_blank">${window.location.origin}${data.redirectPath}</a>`; } else { alert(data.message); } } catch (err) { alert('Failed to save URL. Please try again.'); } }); </script> </body> </html>
This one ties into user authentication, so I'll assume you already have a User collection set up (if not, I'll include a basic schema below).
1. MongoDB 数据模型设计
We have two scalable options here:
- Option 1: Add a
pdfUrlfield directly to your existingUserschema (simple for single PDF per user) - Option 2: Create a separate
UserDocumentcollection (better if users might have multiple PDFs later)
Let's go with Option 2 for future flexibility:
1.1 User Schema (if you don't have one)
// models/User.js const mongoose = require('mongoose'); const userSchema = new mongoose.Schema({ email: { type: String, required: true, unique: true }, password: { type: String, required: true }, name: { type: String, required: true }, createdAt: { type: Date, default: Date.now } }); module.exports = mongoose.model('User', userSchema);
1.2 UserDocument Schema
// models/UserDocument.js const mongoose = require('mongoose'); const userDocumentSchema = new mongoose.Schema({ userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true }, pdfUrl: { type: String, required: true, trim: true }, fileName: { type: String, required: true, trim: true }, createdAt: { type: Date, default: Date.now } }); // Add index to speed up user-specific queries userDocumentSchema.index({ userId: 1 }); module.exports = mongoose.model('UserDocument', userDocumentSchema);
2. Node.js 后端实现
First, we'll set up JWT authentication (the standard for Node.js apps) to protect the PDF endpoint.
2.1 Authentication Middleware
// middleware/auth.js const jwt = require('jsonwebtoken'); const User = require('../models/User'); const authenticateUser = async (req, res, next) => { const token = req.headers.authorization?.split(' ')[1]; // Extract token from "Bearer <token>" if (!token) { return res.status(401).json({ message: 'Authentication required' }); } try { const decoded = jwt.verify(token, process.env.JWT_SECRET); // Use your secret from environment variables req.user = await User.findById(decoded.userId).select('-password'); // Exclude password from user object next(); } catch (err) { res.status(401).json({ message: 'Invalid or expired token' }); } }; module.exports = authenticateUser;
2.2 Endpoints
2.2.1 Login Endpoint (to get JWT token)
// routes/authRoutes.js const express = require('express'); const router = express.Router(); const bcrypt = require('bcryptjs'); const jwt = require('jsonwebtoken'); const User = require('../models/User'); router.post('/login', async (req, res) => { try { const { email, password } = req.body; const user = await User.findOne({ email }); if (!user) { return res.status(400).json({ message: 'Invalid credentials' }); } const isPasswordValid = await bcrypt.compare(password, user.password); if (!isPasswordValid) { return res.status(400).json({ message: 'Invalid credentials' }); } // Generate JWT token valid for 24 hours const token = jwt.sign({ userId: user._id }, process.env.JWT_SECRET, { expiresIn: '24h' }); res.json({ success: true, token, userId: user._id }); } catch (err) { res.status(500).json({ message: 'Server error' }); } }); module.exports = router;
2.2.2 Get User's PDF URL
// routes/userDocumentRoutes.js const express = require('express'); const router = express.Router(); const authenticateUser = require('../middleware/auth'); const UserDocument = require('../models/UserDocument'); // Fetch the user's exclusive PDF router.get('/my-pdf', authenticateUser, async (req, res) => { try { const document = await UserDocument.findOne({ userId: req.user._id }); if (!document) { return res.status(404).json({ message: 'No PDF found for your account' }); } res.json({ success: true, pdfUrl: document.pdfUrl, fileName: document.fileName }); } catch (err) { res.status(500).json({ message: 'Server error' }); } }); module.exports = router;
Register these routes in your main app:
// app.js const authRoutes = require('./routes/authRoutes'); const userDocumentRoutes = require('./routes/userDocumentRoutes'); app.use('/auth', authRoutes); app.use('/documents', userDocumentRoutes);
3. 前端实现
Create a login page, and after successful authentication, a page with the personalized PDF download button:
3.1 Login Page
<!-- login.html --> <!DOCTYPE html> <html> <head> <title>Login</title> <style> .container { max-width: 400px; margin: 2rem auto; padding: 0 1rem; } input, button { padding: 0.8rem; margin: 0.5rem 0; width: 100%; box-sizing: border-box; border-radius: 4px; border: 1px solid #ddd; } button { background: #007bff; color: white; border: none; cursor: pointer; } button:hover { background: #0056b3; } </style> </head> <body> <div class="container"> <h2>Login to Access Your PDF</h2> <form id="loginForm"> <input type="email" id="email" placeholder="Enter your email" required> <input type="password" id="password" placeholder="Enter your password" required> <button type="submit">Login</button> </form> </div> <script> const form = document.getElementById('loginForm'); form.addEventListener('submit', async (e) => { e.preventDefault(); const email = document.getElementById('email').value; const password = document.getElementById('password').value; try { const res = await fetch('/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, password }) }); const data = await res.json(); if (data.success) { localStorage.setItem('token', data.token); window.location.href = '/pdf-download.html'; } else { alert(data.message); } } catch (err) { alert('Login failed. Please try again.'); } }); </script> </body> </html>
3.2 PDF Download Page
<!-- pdf-download.html --> <!DOCTYPE html> <html> <head> <title>Your Exclusive PDF</title> <style> .container { max-width: 600px; margin: 2rem auto; padding: 0 1rem; text-align: center; } .download-btn { padding: 1rem 2rem; background: #28a745; color: white; border: none; border-radius: 4px; font-size: 1.1rem; cursor: pointer; text-decoration: none; display: inline-block; margin-top: 2rem; } .download-btn:hover { background: #218838; } .error { color: #dc3545; margin-top: 2rem; } </style> </head> <body> <div class="container"> <h2>Welcome, Your PDF is Ready!</h2> <div id="downloadSection" style="display: none;"> <a id="pdfLink" class="download-btn" href="#" download>Download Your PDF</a> </div> <div id="errorMsg" class="error" style="display: none;"></div> </div> <script> window.addEventListener('load', async () => { const token = localStorage.getItem('token'); if (!token) { window.location.href = '/login.html'; return; } try { const res = await fetch('/documents/my-pdf', { headers: { 'Authorization': `Bearer ${token}` } }); const data = await res.json(); if (data.success) { const pdfLink = document.getElementById('pdfLink'); pdfLink.href = data.pdfUrl; pdfLink.download = data.fileName; // Forces download instead of opening in browser document.getElementById('downloadSection').style.display = 'block'; } else { document.getElementById('errorMsg').textContent = data.message; document.getElementById('errorMsg').style.display = 'block'; } } catch (err) { document.getElementById('errorMsg').textContent = 'Failed to load your PDF. Please try again later.'; document.getElementById('errorMsg').style.display = 'block'; } }); </script> </body> </html>
Quick Security & Best Practice Notes
- Always use environment variables (with
dotenvpackage) for sensitive data like JWT secrets and MongoDB URIs. - For PDF storage, use cloud services like AWS S3 or Google Cloud Storage, and store signed/public URLs in MongoDB. Avoid storing PDFs directly in the database.
- Hash user passwords before saving them to MongoDB (use
bcryptjsas shown in the login endpoint).
内容的提问来源于stack exchange,提问作者Sahil Jeet Singh

