如何用Koa ctx.body实现多响应?登录API 404及ctx.body失效解决
Hey there! Let's figure out why you're hitting that 404 and having trouble with ctx.body not working. I've looked at your code, and there are a few key issues causing these problems—let's break them down and fix them step by step.
Key Issues in Your Code
- Mixing async/await with callbacks: You're using a callback for
ctx.app.pool.query, but since your route handler is anasyncfunction, Koa doesn't wait for the callback to finish before sending a response. That's why you get a 404—Koa thinks the request is done before your query and password check complete, so it sends a default 404 becausectx.bodywas never set in time. - Unhandled empty query result: When the email doesn't exist in the database,
resultis still a valid object (just with an emptyrowsarray), so yourelseclause forif (result)never runs. This leavesctx.bodyunset, leading to a 404. - Inconsistent async patterns: You're using
awaitin some places but.then()forbcrypt.compare, which makes it harder to track when the response gets set and can lead to race conditions.
Fixing the Code
Let's rewrite your route handler to use consistent async/await (no callbacks!) and handle all edge cases properly:
router.post('/userLogin', async (ctx) => { const { email, password } = ctx.request.body; // Check for missing fields if (!email || !password) { ctx.status = 400; ctx.body = { status: 'error', message: 'Please fill all the fields' }; return; // Important: stop execution here so we don't run the rest of the code } try { // Use await for the query (pg's query returns a promise!) const result = await ctx.app.pool.query("SELECT * FROM users WHERE email = $1", [email]); // Check if any user was found if (result.rows.length === 0) { ctx.status = 400; ctx.body = { status: 'error', message: "Invalid email" }; return; } // Use await for bcrypt.compare instead of .then() const isPasswordMatch = await bcrypt.compare(password, result.rows[0].password); if (isPasswordMatch) { ctx.status = 200; ctx.body = { status: 'success', message: "User login successfully", data: result.rows[0] }; } else { ctx.status = 400; ctx.body = { status: 'error', message: "Incorrect password" }; } } catch (err) { console.error(err); ctx.status = 500; ctx.body = { status: 'error', message: "Server error" }; } });
How to Handle Multiple Responses in Koa
Koa only allows one response per request—you can't send multiple responses. Instead, structure your code to cover all possible paths (success, missing fields, invalid credentials, server errors) and set ctx.body + ctx.status in each path. Use return after setting the response to make sure no further code runs that might overwrite the response.
Troubleshooting Steps to Fix 404 & ctx.body Issues
- Verify route mounting: Make sure you've added the router to your Koa app with:
app.use(router.routes()); app.use(router.allowedMethods()); - Check request URL: Ensure you're sending a POST request to exactly
/userLogin(case-sensitive!)—a typo like/userloginor/loginwill trigger a 404. - Log execution paths: Add
console.logstatements in each branch (e.g.,console.log("Missing fields"),console.log("User found")) to confirm which parts of your code are running. This helps you spot if a branch is being skipped. - Avoid callback hell: Always use
awaitfor async operations (like database queries, bcrypt checks) instead of callbacks. This ensures Koa waits for your logic to finish before sending a response. - Handle all edge cases: Make sure every possible scenario (missing fields, invalid email, wrong password, server error) has a
ctx.bodyandctx.statusset. No path should leavectx.bodyundefined.
内容的提问来源于stack exchange,提问作者Daniyal Mughees

