React应用搭配Express后端:登录/注册等多模块路由实现咨询
Absolutely, splitting your authentication (signIn/signUp) and user data display into separate modules is not just reasonable—it’s a great practice for building maintainable, scalable React + Express apps. Here’s why it makes sense:
- Separation of concerns: Each module focuses on one specific task (auth vs user data), making your code easier to debug, test, and update. For example, you can tweak sign-up validation logic without touching the user profile display code.
- Reusability: Auth modules can be reused across different parts of your app (like protecting React routes), while user data modules can be extended to handle profile edits or admin dashboards later.
- Team collaboration: If you’re working with others, different developers can own different modules without stepping on each other’s toes.
How to configure Express routes for these modules
First, let’s structure your Express app to keep routes organized. We’ll split API routes into dedicated files, then handle React’s static files and SPA fallback properly.
1. Project structure example
your-app/ ├── client/ # React app (build output goes here) ├── server/ │ ├── routes/ │ │ ├── auth.js # signIn/signUp routes │ │ └── users.js # user data routes │ ├── middleware/ │ │ └── auth.js # JWT verification middleware │ └── app.js # Main Express server file
2. Create auth routes (server/routes/auth.js)
This file handles user registration and login, with logic for password hashing, input validation, and JWT token issuance (for authenticated requests later):
const express = require('express'); const router = express.Router(); const bcrypt = require('bcryptjs'); const jwt = require('jsonwebtoken'); const User = require('../models/User'); // Assume you have a User Mongoose model // Sign up route router.post('/signup', async (req, res) => { try { const { email, password, username } = req.body; // Check if user already exists const existingUser = await User.findOne({ email }); if (existingUser) { return res.status(400).json({ message: 'User already exists' }); } // Hash password const hashedPassword = await bcrypt.hash(password, 10); // Create new user const newUser = new User({ email, password: hashedPassword, username }); await newUser.save(); // Issue JWT const token = jwt.sign({ userId: newUser._id }, process.env.JWT_SECRET, { expiresIn: '1d' }); res.status(201).json({ token, user: { id: newUser._id, email: newUser.email, username: newUser.username } }); } catch (err) { res.status(500).json({ message: 'Server error' }); } }); // Sign in route router.post('/signin', 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' }); } const token = jwt.sign({ userId: user._id }, process.env.JWT_SECRET, { expiresIn: '1d' }); res.json({ token, user: { id: user._id, email: user.email, username: user.username } }); } catch (err) { res.status(500).json({ message: 'Server error' }); } }); module.exports = router;
3. Create user data routes (server/routes/users.js)
This file handles fetching user data, protected by an authentication middleware to ensure only logged-in users can access sensitive endpoints:
const express = require('express'); const router = express.Router(); const authMiddleware = require('../middleware/auth'); // Your JWT verification middleware const User = require('../models/User'); // Get current user's data router.get('/me', authMiddleware, async (req, res) => { try { // Fetch user without exposing the password field const user = await User.findById(req.userId).select('-password'); if (!user) { return res.status(404).json({ message: 'User not found' }); } res.json(user); } catch (err) { res.status(500).json({ message: 'Server error' }); } }); // Get user by ID (for public profiles or admin use, adjust permissions as needed) router.get('/:id', async (req, res) => { try { const user = await User.findById(req.params.id).select('-password'); if (!user) { return res.status(404).json({ message: 'User not found' }); } res.json(user); } catch (err) { res.status(500).json({ message: 'Server error' }); } }); module.exports = router;
4. Configure main Express server (server/app.js)
Here, we’ll mount the API routes, serve React’s static build files, and handle SPA fallback (return index.html for all non-API requests):
const express = require('express'); const cors = require('cors'); const path = require('path'); const authRoutes = require('./routes/auth'); const userRoutes = require('./routes/users'); const app = express(); // Middleware app.use(cors()); // Allow cross-origin requests (if React runs on a different port) app.use(express.json()); // Parse JSON request bodies // Mount API routes app.use('/api/auth', authRoutes); app.use('/api/users', userRoutes); // Serve React static files (after running `npm run build` in your React app) app.use(express.static(path.join(__dirname, '../client/build'))); // SPA fallback: return index.html for all non-API requests app.get('*', (req, res) => { res.sendFile(path.join(__dirname, '../client/build/index.html')); }); const PORT = process.env.PORT || 5000; app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
5. Key notes for React integration
- In your React app, create separate service modules (e.g.,
AuthService.jsfor signIn/signUp calls,UserService.jsfor fetching user data) to encapsulate API requests. Example:// AuthService.js export const signUp = (userData) => { return fetch('/api/auth/signup', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(userData) }).then(res => res.json()); }; - Use React Context or state management (like Redux) to store the JWT token and user data, so you can attach the token to authenticated requests (e.g., adding an
Authorization: Bearer ${token}header inUserService.js).
内容的提问来源于stack exchange,提问作者Austris

