Routing system and URL processing
The routing system in the Express framework is the core mechanism for handling HTTP requests. It maps client requests to specific handler functions through a combination of URL paths and HTTP methods. URL processing involves key functionalities such as path matching, parameter extraction, and query string parsing, which directly impact the API design and resource location efficiency of web applications.
Basic Routing Structure
Express routes consist of three parts: an HTTP method, a path pattern, and a callback function. Basic routes are defined using the app.METHOD(path, handler)
format, for example:
const express = require('express');
const app = express();
// Basic GET route
app.get('/products', (req, res) => {
res.send('Product list');
});
// Route with parameters
app.post('/users/:id', (req, res) => {
console.log(req.params.id);
res.status(201).send();
});
Routing methods support all HTTP verbs, including get
, post
, put
, delete
, etc., and also provide the all
method to handle requests of any method:
app.all('/secure', (req, res) => {
console.log(`Received ${req.method} request`);
res.sendStatus(403);
});
Route Path Matching Rules
Express supports various path matching patterns:
- String paths:
/about
- String patterns:
/ab?cd
(matches acd or abcd) - Regular expressions:
/.*fly$/
- Parameterized paths:
/users/:userId
Example of complex path matching:
// Multi-segment parameters
app.get('/books/:category/:id', (req, res) => {
const { category, id } = req.params;
res.json({ category, id });
});
// Regex constraints
app.get('/user/:id(\\d+)', (req, res) => {
res.send(`User ID: ${req.params.id}`);
});
// Wildcard paths
app.get('/files/*', (req, res) => {
res.send(`Requested file path: ${req.params[0]}`);
});
Route Parameter Handling
Path parameters are exposed via the req.params
object, supporting optional parameters and multiple parameter segments:
// Optional parameters
app.get('/archive/:year?/:month?', (req, res) => {
const { year = '2023', month = '01' } = req.params;
res.send(`${year}-${month} archive`);
});
// Parameter validation middleware
const validateId = (req, res, next) => {
if (!/^\d+$/.test(req.params.id)) {
return res.status(400).send('Invalid ID format');
}
next();
};
app.get('/api/items/:id', validateId, (req, res) => {
// Process valid ID
});
Query String Parsing
URL query parameters are automatically parsed into the req.query
object, supporting complex structures:
// Handle /search?q=express&page=2
app.get('/search', (req, res) => {
const { q, page = 1 } = req.query;
res.send(`Search "${q}", page ${page}`);
});
// Array parameters /filters?color=red&color=blue
app.get('/filters', (req, res) => {
console.log(req.query.color); // ['red', 'blue']
});
Modular Route Organization
Express.Router class enables modular routing:
// routes/users.js
const router = require('express').Router();
router.get('/', (req, res) => {
res.send('User list');
});
router.get('/:id', (req, res) => {
res.send(`User details: ${req.params.id}`);
});
module.exports = router;
// Main file
const userRoutes = require('./routes/users');
app.use('/users', userRoutes);
Supports route-level middleware and path prefixes:
const apiRouter = express.Router();
// Apply API authentication middleware
apiRouter.use(authenticate);
apiRouter.get('/data', (req, res) => {
res.json({ data: 'Sensitive information' });
});
app.use('/api/v1', apiRouter);
Route Middleware System
Route handling supports multiple middleware functions, forming a processing chain:
const logRequest = (req, res, next) => {
console.log(`${req.method} ${req.path}`);
next();
};
const validateData = (req, res, next) => {
if (!req.body) return res.status(400).send('Request body required');
next();
};
app.post('/orders', logRequest, validateData, (req, res) => {
// Process valid order
res.status(201).json({ id: Date.now() });
});
Error-handling middleware example:
app.get('/danger', (req, res, next) => {
try {
throw new Error('Simulated error');
} catch (err) {
next(err);
}
});
// Error-handling middleware
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Server error');
});
Route Loading Order
Express matches routes in the order they are defined, so priority matters:
// This route will intercept all /admin paths
app.get('/admin*', (req, res) => {
res.send('Admin area');
});
// This route will never be triggered
app.get('/admin/settings', (req, res) => {
res.send('Admin settings');
});
Correct approach: place specific paths first:
app.get('/admin/settings', (req, res) => {
res.send('Admin settings');
});
app.get('/admin*', (req, res) => {
res.send('Other admin pages');
});
Dynamic Route Loading
Example of loading route modules on demand:
const fs = require('fs');
const path = require('path');
const routesDir = path.join(__dirname, 'routes');
fs.readdirSync(routesDir).forEach(file => {
const routePath = path.join(routesDir, file);
const route = require(routePath);
const baseName = path.basename(file, '.js');
app.use(`/${baseName}`, route);
console.log(`Loaded route: /${baseName}`);
});
Route Performance Optimization
Optimization strategies for large-scale routing tables:
// Use route tables instead of continuous registration
const routes = [
{ method: 'GET', path: '/api/v1/users', handler: getUsers },
{ method: 'POST', path: '/api/v1/users', handler: createUser }
];
routes.forEach(({ method, path, handler }) => {
app[method.toLowerCase()](path, handler);
});
// Path caching middleware
const routeCache = {};
app.use((req, res, next) => {
const cacheKey = `${req.method}:${req.path}`;
if (routeCache[cacheKey]) {
return routeCache[cacheKey](req, res);
}
next();
});
Advanced Path Matching Techniques
Complete example of RESTful resource routing:
const resources = ['posts', 'comments', 'likes'];
resources.forEach(resource => {
const controller = require(`./controllers/${resource}`);
app.route(`/api/${resource}`)
.get(controller.index)
.post(controller.create);
app.route(`/api/${resource}/:id`)
.get(controller.show)
.put(controller.update)
.delete(controller.destroy);
});
Route handling with content negotiation:
app.get('/data', (req, res) => {
res.format({
'text/plain': () => {
res.send('Text data');
},
'application/json': () => {
res.json({ data: 'JSON data' });
},
default: () => {
res.status(406).send('Unsupported format');
}
});
});
Integration with View Systems
Typical application of dynamic routes combined with template engines:
app.set('view engine', 'ejs');
app.get('/products/:slug', async (req, res) => {
try {
const product = await db.findProductBySlug(req.params.slug);
res.render('product-detail', { product });
} catch (err) {
res.status(404).render('404');
}
});
Modern Routing Practices
Using async/await in route handlers:
app.get('/async-data', async (req, res, next) => {
try {
const data = await fetchRemoteData();
const processed = await processData(data);
res.json(processed);
} catch (err) {
next(err);
}
});
Promise-based route error handling:
const asyncHandler = fn => (req, res, next) => {
Promise.resolve(fn(req, res, next))
.catch(next);
};
app.get('/promise-route', asyncHandler(async (req, res) => {
const result = await complexOperation();
res.send(result);
}));
Route Debugging Techniques
Route debugging methods during development:
// Display all registered routes
app._router.stack.forEach(layer => {
if (layer.route) {
const methods = Object.keys(layer.route.methods)
.filter(method => layer.route.methods[method])
.join(', ').toUpperCase();
console.log(`${methods} ${layer.route.path}`);
}
});
// Route tracing middleware
app.use((req, res, next) => {
console.log(`[${new Date().toISOString()}] ${req.method} ${req.path}`);
next();
});
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
下一篇:中间件机制与执行流程