iT邦幫忙

2025 iThome 鐵人賽

DAY 28
0
Modern Web

【網頁是什麼,能吃嗎】── 零基礎也能學會網頁製作系列 第 28

【Day 28】為昨天的程式 Debug ── Vue.js 實作篇

  • 分享至 

  • xImage
  •  

Bug / 問題說明

  • 按下 Login 之後:POST http://localhost:3000/api/auth/login 404 (Not Found)
  • 前端丟出錯誤:Unexpected token '<', "<!DOCTYPE "... is not valid JSON
    → 表示我在 apiFetch 嘗試 JSON.parse(),但伺服器回來的是 HTML(以 <!DOCTYPE html> 開頭)。

我目前的推測

  • 請求確實打到某個 :3000,但不是我那個 Express app 的 /api/auth/login
  • 可能性:
    1. 另一個服務佔著 :3000,回了 HTML(像 SPA 的 index.html)。
    2. 我的 Express 路由沒有正確掛上,導致打到 fallback/catch-all,回 HTML。

我做了哪些檢查

1) 前端呼叫與 payload

export const API_BASE = "http://localhost:3000";

const data = await apiFetch('/api/auth/login', {
  method: 'POST',
  body: JSON.stringify({
    // 若後端預期 usernameOrEmail,這裡要對齊
    usernameOrEmail: username.value,
    password: password.value
  })
});
  • 把驗證從 if (!username && !password) 改成 if (!username || !password)(小地方但對的)。
  • path 一定用 /api/...(要有前導 /)。

2) 後端路由掛載

// server/index.js
app.use('/api/auth', authRouter); // prefix 正確

// server/routes/userAuth.js
router.post('/login', handler);    // 不要再寫成 '/api/auth/login'
export default router;

→ 最終端點應該是 POST /api/auth/login

3) 想看請求有沒有真的打到我的 app

我在最上面加了一個 logger:

app.use((req, _res, next) => { 
  console.log('REQ', req.method, req.path); 
  next(); 
});
  • 但按 Login 沒有看到 REQ POST /api/auth/login
  • 直接在瀏覽器開 http://localhost:3000/api/ping 得到 200卻也沒印 REQ GET /api/ping

明日計畫

  1. 換一個埠來驗證

    • 先把 API 改成 4000
      // server/index.js
      const port = 4000;
      
    • 前端改為:
      export const API_BASE = 'http://localhost:4000';
      
    • 重新啟動前後端 → 開 http://localhost:4000/api/ping
      應該要看到 REQ GET /api/ping
  2. 列出實際有掛的路由(dev only)

    app.get('/__routes', (_req, res) => {
      const routes = [];
      app._router.stack.forEach((layer) => {
        if (layer.route) {
          routes.push({
            path: layer.route.path,
            methods: Object.keys(layer.route.methods).map(m => m.toUpperCase())
          });
        } else if (layer.name === 'router' && layer.handle.stack) {
          layer.handle.stack.forEach((r) => {
            if (r.route) {
              routes.push({
                path: r.route.path,
                methods: Object.keys(r.route.methods).map(m => m.toUpperCase())
              });
            }
          });
        }
      });
      res.json(routes);
    });
    
    • http://localhost:4000/__routes
      → 應該要看到 "/login"POST),代表 router 有掛好。
  3. 先加一個「已知可達」的最小路由(排除 router import 問題)

    // 先直接在 server/index.js 補一條
    app.post('/api/auth/login', (req, res) => {
      res.json({ ok: true, reached: true, body: req.body || null });
    });
    
    • 如果這條能回 JSON 並且印出 REQ POST /api/auth/login
      → 代表 userAuth.js 的匯入 / 導出或子路徑有誤。
    • 如果仍然沒印,就 100% 是打錯伺服器/埠
  4. 必要時找出佔用 3000 的進程

    ss -ltnp | grep :3000
    # 或
    lsof -i :3000
    
    • 確認 PID 與程式名稱,關掉它或換埠。

小備忘(給未來的我)

  • API 路由一定要在 static / catch-all 之前掛上,不然 /api/* 會被 index.html 吞掉。
  • 前端 credentials: 'include' + 後端 cors({ origin, credentials: true }) 兩邊都要設。
  • 後端若期待 usernameOrEmail,前端就別送 username
  • 「看到 <!DOCTYPE html>」= 你在解析 HTML 當 JSON,通常是 404/錯伺服器/錯掛載

上一篇
【Day 27】後端程式碼整理 ── Vue.js 實作篇
系列文
【網頁是什麼,能吃嗎】── 零基礎也能學會網頁製作28
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言