Back to Review Hub 返回复习中心
DAY 1 / 第1天

🚀 Deep Dive: Express.js Routing 🚀 深度解析:Express.js 路由

Mastering the foundation of modern web APIs in COMP1531. 掌握 COMP1531 中现代 Web API 的基石。

1. Initialization & The "Invisible" Logic 1. 初始化与“不可见”的逻辑

Before handling any routes, every Express application needs a skeleton. The most critical part is middleware. 在处理任何路由之前,每个 Express 应用程序都需要一个骨架。最关键的部分是中间件

import express from 'express';
const app = express();
const PORT = 3000;

// CRITICAL: Middleware to parse JSON bodies
// Without this, req.body will be undefined!
app.use(express.json()); 

// Basic "Hello World" route
app.get('/', (req, res) => {
  res.json({ message: 'Server is running!' });
});

💡 The app.use() Methodapp.use() 方法

app.use() defines middleware. Middleware functions execute sequentially before the request reaches your specific route handler. express.json() is a built-in middleware that looks at the Content-Type: application/json header and extracts the raw bytes into a JavaScript object. app.use() 定义了中间件。中间件函数在请求到达具体路由处理器之前按顺序执行。express.json() 是一个内置中间件,它会检查 Content-Type: application/json 请求头,并将原始字节提取到 JavaScript 对象中。

Visual Flow: The Middleware Pipeline 视觉流程:中间件流水线
INPUT HTTP Request
MIDDLEWARE express.json()
LOGIC Route Handler
OUTPUT HTTP Response

2. Capturing Data: Body, Params, and Query 2. 捕获数据:Body、Params 与 Query

Express provides three distinct ways to extract data from a request. Choosing the right one is key to good API design. Express 提供了三种不同的方式从请求中提取数据。选择正确的方式是优秀 API 设计的关键。

Variable变量 Extraction Type提取类型 Data Format数据格式 Best Practice最佳实践
req.body Payload载荷 Objects/Arrays对象/数组 New data for POST/PUTPOST/PUT 的新数据
req.params Route Variables路由变量 Strings (IDs)字符串 (ID) Specific Resource identification特定资源识别
req.query Question Mark URL问号 URL Key-Value pairs键值对 Filtering and Sorting过滤和排序
Visual Guide: Data Extraction 视觉指南:数据提取
domain.com/order/:orderid?status=complete
PATH
req.params
{ orderid: "123" }
QUERY
req.query
{ status: "complete" }
BODY (JSON)
req.body
{ name: "Evan" }

Practical Examples实际示例

// 1. Using req.params (Path Variables)
// URL: /user/123/profile
app.get('/user/:userId/profile', (req, res) => {
  const id = parseInt(req.params.userId as string);
  res.json({ userId: id, name: 'Evan' });
});

// 2. Using req.query (Search Parameters)
// URL: /search?type=admin&sort=desc
app.get('/search', (req, res) => {
  const { type, sort } = req.query;
  res.json({ results: [], type, sort });
});

// 3. Using req.body (JSON Payload)
// Method: POST | URL: /register | Body: {"email": "..."}
app.post('/register', (req, res) => {
  const { email, password } = req.body;
  // Logic to save to data store
  res.json({ status: 'Success' });
});

⚠️ The "String" Trap“字符串”陷阱

Both req.params and req.query are parsed directly from the URL. This means they are **ALWAYS** strings. If your data store uses numerical IDs, you must use parseInt() or Number() before comparison. Comparing "123" === 123 will return false! req.paramsreq.query 都是直接从 URL 解析的。这意味着它们始终是字符串。如果你的数据存储使用数字 ID,你在比较之前必须使用 parseInt()Number()。比较 "123" === 123 将返回 false

3. The Parameter Showdown (Crucial for Exams!) 3. 三种“参数”大比拼 (面试/考试必考! )

To clear any final confusion, here is a definitive comparison table of the three ways to pass data: 为了让你不再混淆,我们看这张对比表:

Source来源 Example位置示例 Extraction Code在 Express 中拿取的代码 Usage Scenarios适用场景
Path Params /user/123 req.params.id Identify a unique resource (e.g., a specific user) 唯一定位一个资源 (如:看哪个特定用户)
Query Params /user?id=123 req.query.id Filtering or searching results 过滤/搜索结果 (如:按 ID 搜索用户)
Body (JSON) { "id": 123 } req.body.id Submitting/modifying large or sensitive data 提交或修改大量数据、敏感数据

4. The First Match Wins: Route Order 4. 首位匹配权:路由顺序

Express matches routes in the order they are defined (Top-to-Bottom). Specific routes must come BEFORE generic routes. Express 按定义顺序(从上到下)匹配路由。具体路由必须在通用路由之前定义。

Incorrect Order错误顺序

// Generic route matches first
app.get('/user/:id', ...);

// This will NEVER be reached!
// 'me' is treated as an ':id'
app.get('/user/me', ...);
                    

Correct Order正确顺序

// Specific route first
app.get('/user/me', ...);

// Generic route second
app.get('/user/:id', ...);
                    

5. Common Pitfalls & Professional Tips 5. 常见陷阱与专业建议

🚫 Multiple Response Error多重响应错误

If you call res.json() and then let the code continue into another res.json(), the server will crash with ERR_HTTP_HEADERS_SENT. 如果你调用 res.json() 后让代码继续执行到另一个 res.json(),服务器会崩溃并提示 ERR_HTTP_HEADERS_SENT

if (error) {
  res.status(400).json({ error });
  return; // DO NOT REMOVE THIS RETURN!
}
res.json({ data: 'Success' });

🏆 Best Practices最佳实践

  • Consistency: Always use res.status(400).json() for errors in COMP1531. Don't mix res.send and res.json. 一致性:在 COMP1531 中始终对错误使用 res.status(400).json()。不要混用 res.sendres.json
  • Explicit Returns: Always return after sending an error response to guarantee no further execution. 显式返回:发送错误响应后始终返回,以确保不再执行后续代码。
  • Route Naming: Use nouns for resources (e.g., /order/new) and follow the RESTful mindset even if not fully implementing it yet. 路由命名:对资源使用名词(例如 /order/new),即使尚未完全实现,也要遵循 RESTful 思路。
  • Testing: Use sync-request-curl for black-box testing. Ensure it covers success (200) and failure (400) cases. 测试:使用 sync-request-curl 进行黑盒测试。确保它涵盖了成功 (200) 和失败 (400) 的情况。

✅ 6. Day 1 Mastery Checklist ✅ 6. 第1天掌握清单

  • 🏁 Created an Express app and set a PORT.创建了 Express 应用并设置了端口。
  • 🏁 Added app.use(express.json()) middleware.添加了 app.use(express.json()) 中间件。
  • 🏁 Implemented GET and POST routes.实现了 GET 和 POST 路由。
  • 🏁 Parsed numbers from path parameters.从路径参数中解析了数字。
  • 🏁 Returned 400 status on data validation failure.在数据验证失败时返回了 400 状态码。
  • 🏁 Understood why 'return' is needed after error res.理解了为什么在错误响应后需要 'return'。

📝 Comprehensive Day 1 Mastery Quiz 📝 第1天深度复习测验

Test your understanding of route parameters, matching order, and error handling. 测试你对路由参数、匹配顺序和错误处理的理解。

🎯 Start 50-Question Challenge 🎯 开始 50 题挑战