歡迎來到第十二天!過去幾天,我們一直專注在 RAG 和向量資料庫上,教會了 AI 如何「理解」使用者在說什麼。我們的 AI 面試官現在有了不錯的「文科」素養,能針對概念題進行有深度的語意分析。
但是,一個優秀的前端面試官,光有文科素養是不夠的,還必須具備紮實的「理科」能力——也就是判斷程式碼對錯的能力。如果一段程式碼連最基本的 console.log('Hello World')
都印不出來,那即便它在語意上看起來寫得再怎麼「漂亮」,也是不及格的。
我們需要為 AI 補上這塊短板:在它進行主觀的 Code Review 之前,先提供給它客觀的執行結果。
然而,直接在我們的伺服器上執行使用者提交的程式碼,是極度危險的行為。想像一下,如果有人提交了惡意程式碼,比如 fs.unlinkSync('./.env.local')
,那我們的金鑰和整個專案就都毀了。這絕對不行!
因此,我們需要一個專業、安全、隔離的「程式碼沙箱」服務。今天,我們就來認識並整合這樣一個強大的工具:Judge0。
Judge0 是一個開源的、強大的「程式碼執行即服務 (Code Execution as a Service)」平台。你可以把它想像成一個專門用來執行程式碼的遠端實驗室。
它的核心工作流程非常簡單:
stdin
) 等資訊,透過 API 傳送給它。stdout
)stderr
)為什麼不自己做,而要用 Judge0?
痛點 | Judge0 如何解決 | 前端類比 |
---|---|---|
安全性 | 使用者程式碼在與你伺服器完全隔離的沙箱中執行,無法存取你的檔案、環境變數或網路。 | 就像在 <iframe> 中使用 sandbox 屬性,限制了子頁面的權限,但 Judge0 的隔離更徹底。 |
複雜性 | 你不需要自己管理 Docker、設定資源限制 (CPU/Memory)、處理各種語言的執行環境。 | 就像使用 Next.js 或 Vite,你不需要從零開始設定 Webpack 和 Babel,工具已經幫你處理好了。 |
資源消耗 | 執行程式碼是個消耗資源的任務,外包給 Judge0 可以讓你的伺服器專注於處理 API 請求。 | 就像將圖片、影片等靜態資源放在 CDN 上,減輕主伺服器的負擔。 |
多語言支援 | 開箱即用支援數十種程式語言,未來擴充題庫到 Python 或其他語言也沒問題。 | 像是 Prettier 或 ESLint ,透過插件就能支援不同的程式碼風格與語言。 |
總之,使用 Judge0 讓我們能專注在應用程式的邏輯上,而把最棘手、最危險的程式碼執行任務,交給最專業的工具來處理。
Judge0 官方推薦透過 RapidAPI 這個平台來使用他們的服務,因為它提供了一個非常慷慨的免費方案,對我們的專案來說綽綽有餘。
前往 RapidAPI 上的 Judge0 頁面
請打開 Judge0 CE on RapidAPI。
註冊並訂閱免費方案
用你的 GitHub 或 Google 帳號註冊/登入。接著,點擊「Pricing」分頁,找到「BASIC」方案(通常是免費的),點擊「Subscribe」訂閱它。
找到你的 API 金鑰
回到「Endpoints」分頁。在頁面的右側,你會看到程式碼範例區塊。在這裡,你可以找到兩個我們需要的關鍵資訊:
X-RapidAPI-Key
: 這是你的個人金鑰,像密碼一樣重要。X-RapidAPI-Host
: judge0-ce.p.rapidapi.com
,這是服務的主機位置。![]() |
---|
圖1 : 在 RapidAPI 找到你的金鑰與主機 |
儲存到環境變數
和 Gemini 的金鑰一樣,我們必須把這些敏感資訊儲存在 .env.local
檔案中,從後端讀取而不直接在前端顯示出來,再次強調,包在前端的資訊永遠不安全的,別再傻傻的以為環境可以在前端藏住了。
打開 .env.local
,加入以下兩行:
# .env.local
# ... 其他金鑰 ...
JUDGE0_API_HOST=judge0-ce.p.rapidapi.com
JUDGE0_API_KEY=貼上你剛剛複製的X-RapidAPI-Key
現在我們有了金鑰,但千萬記得我們的安全第一原則:金鑰永不下發到瀏覽器。
我們必須建立一個自己的後端 API Route 來當作「中間人」或「代理」。前端只會跟我們自己的 API 溝通,再由我們的後端,帶著金鑰去跟真正的 Judge0 API 溝通。
這樣做的好處是:
JUDGE0_API_KEY
永遠只存在於我們的伺服器端,前端完全接觸不到。讓我們來建立第一個測試用的代理,功能是獲取 Judge0 支援的所有程式語言。
在 app/api/
資料夾下,建立新資料夾 judge0
,並在其中新增 languages/route.ts
:
// app/api/judge0/languages/route.ts
import { NextResponse } from 'next/server';
const JUDGE0_API_HOST = process.env.JUDGE0_API_HOST;
const JUDGE0_API_KEY = process.env.JUDGE0_API_KEY;
export async function GET() {
// 再次檢查環境變數是否存在,確保伺服器配置正確
if (!JUDGE0_API_HOST || !JUDGE0_API_KEY) {
return NextResponse.json(
{ error: 'Judge0 API 環境變數未設定' },
{ status: 500 }
);
}
const url = `https://${JUDGE0_API_HOST}/languages`;
const options = {
method: 'GET',
headers: {
'X-RapidAPI-Key': JUDGE0_API_KEY,
'X-RapidAPI-Host': JUDGE0_API_HOST,
},
};
try {
const response = await fetch(url, options);
// 如果 Judge0 回傳非 200 的狀態碼,我們將錯誤資訊透傳給前端
if (!response.ok) {
const errorData = await response.text(); // 使用 .text() 以防回傳的不是 JSON
return NextResponse.json(
{ error: '無法從 Judge0 獲取資料', details: errorData },
{ status: response.status }
);
}
const data = await response.json();
// 成功取得資料後,將其回傳給前端
return NextResponse.json(data);
} catch (error) {
console.error('代理請求至 Judge0 時發生網路或解析錯誤:', error);
return NextResponse.json(
{ error: '代理伺服器內部錯誤' },
{ status: 500 }
);
}
}
X-RapidAPI-Key
和 X-RapidAPI-Host
。萬事俱備,只欠測試!打開我們的本地專案:
npm run dev
打開瀏覽器,訪問我們剛剛建立的代理 API 端點:http://localhost:3000/api/judge0/languages
如果一切順利,你應該會看到一個 JSON 陣列,裡面列出了 Judge0 支援的所有語言,類似這樣:
[
{
"id": 63,
"name": "JavaScript (Node.js 12.14.0)"
},
{
"id": 71,
"name": "Python (3.8.1)"
},
{
"id": 74,
"name": "TypeScript (3.7.4)"
},
// ... and many more
]
這就證明了我們的後端代理已經成功運作!我們的 Next.js 應用程式現在具備了與 Judge0 安全通訊的能力。
補充說明:常見坑、降級策略與成本提示
成本提示:RapidAPI 的免費方案是每天 200 次提交 (Submission),速率限制為每分鐘 5 次。對於我們的開發和測試來說非常足夠。GET /languages 這種查詢型 API 通常不計入提交次數,但還是要注意不要頻繁呼叫。
我們的代理 API 已經做了基本的錯誤處理。但更進一步的降級策略是什麼?
如果 Judge0 服務暫時不可用(例如 API 掛了,或我們超出了免費額度),我們的 /api/judge0/execute(明天會實作)應該要能優雅地失敗。它不應該讓整個應用程式崩潰,而是回傳一個特定的狀態,例如 { "status": "degraded", "message": "Code execution service unavailable" }。
當 AI 收到這個狀態時,我們會在 Prompt 裡指示它:「注意:無法取得客觀執行結果。請僅根據程式碼的邏輯、風格和可讀性進行評論,並在回饋中註明此程式碼未經實際運行。」
這就是一個穩健的降級路徑,確保即使部分外部服務失效,我們的核心功能依然能以一種降級但可用的方式運作。
今天我們為專案的「理科」能力打下了堅實且安全的基礎。雖然看起來只是建立了一個簡單的代理,但背後的安全原則至關重要。
✅ 我們理解了直接執行使用者程式碼的巨大風險,以及 Judge0 如何解決這個問題。
✅ 我們成功在 RapidAPI 申請了免費的 Judge0 服務並取得了 API 金鑰。
✅ 我們掌握了建立安全後端代理的核心原則,並實作了一個 Next.js API Route 來保護金鑰。
✅ 我們成功地透過代理 API 從 Judge0 獲取了資料,驗證了通訊管道的暢通。
安全通道已經建立,明天(Day 13),我們就要開始傳送真正的「貨物」了。我們將實作最核心的 /api/judge0/execute 代理,學會如何將使用者的 JavaScript 程式碼發送到 Judge0 執行,並取回關鍵的執行結果,例如 stdout、stderr、執行時間等等。
這些客觀的「呈堂證供」,將在 Day 14 成為我們餵給 AI 的最有力證據!我們明天見!