Loopback NodeJS:登录时校验用户角色问题求助
Hey, let's figure out why that RoleMappings API call is throwing a 400 error in your code—even though it works in IBM API Connect. I spotted a few key issues in your snippet that we can fix right away:
1. You're Losing Context for this.headers
In the nested request call, the this keyword doesn't point to your outer context anymore. That means this.headers is probably undefined here, and missing required headers is a common cause of 400 bad requests.
Quick Fix:
Store the headers in a variable outside the nested callback to preserve the context:
// Add this right before your first request call const requestHeaders = this.headers;
Then use requestHeaders instead of this.headers in your adminOptions:
headers: requestHeaders
2. Your Filter Parameter is Broken in Two Ways
First, you've got a syntax error: ""+body.userId"" has an extra closing quote. Second, Loopback expects the filter query param to be a JSON string, not a plain object. When you pass an object directly in qs, the request library turns it into nested query params (like filter[where][principalId]=xxx) which Loopback can't parse correctly.
Quick Fix:
Fix the syntax error and stringify the filter explicitly:
qs: { filter: JSON.stringify({ where: { principalId: body.userId.toString(), // Ensure it's a string matching Cloudant's stored ID roleId: "159882e4a5bdde9cc725eee8c13a1030" } }) }
3. Your Async Logic is Out of Order
Your outer callback(null, body) runs before the nested RoleMappings request finishes. That means you're returning the user data before setting the isAdmin property—even if the 400 error is fixed, this logic bug would still break your feature.
Quick Fix:
Move the final callback inside the nested request's callback so it only runs after the admin check completes:
request(adminOptions, function(aReqErr, aRes, aBody){ if(aReqErr) return callback(aReqErr); if(aBody && aBody.length > 0){ body["isAdmin"] = true; console.log("Admin check success:", aBody); } else { body["isAdmin"] = false; console.log("aResCode", aRes.statusCode); console.log("aResMessage", aRes.statusMessage); } // Return the updated user data only after admin check callback(null, body); });
Bonus Fix: Add Auth Token to the RoleMappings Request
Chances are, your Loopback API uses token-based authentication. The login request sends username/password, but the RoleMappings endpoint requires the access token returned from the login response. This is probably why the API Connect test works (you manually added the token) but your code doesn't.
Update the adminOptions headers to include the bearer token:
var adminOptions = { method: 'GET', url: conf.endpoint + '/api/RoleMappings', qs: { /* ... your stringified filter ... */ }, headers: { ...requestHeaders, 'Authorization': `Bearer ${body.id}` // Attach the login token here } };
Putting all the fixes together:
var options = { method: 'POST', url: conf.endpoint + 'api/Clients/login', headers: this.headers, body: user, json: true }; // Preserve header context const requestHeaders = this.headers; request(options, function (error, response, body) { if (error) return callback(error); var adminOptions = { method: 'GET', url: conf.endpoint + '/api/RoleMappings', qs: { filter: JSON.stringify({ where: { principalId: body.userId.toString(), roleId: "159882e4a5bdde9cc725eee8c13a1030" } }) }, headers: { ...requestHeaders, 'Authorization': `Bearer ${body.id}` } }; request(adminOptions, function(aReqErr, aRes, aBody){ if(aReqErr) return callback(aReqErr); body["isAdmin"] = aBody && aBody.length > 0; console.log(aBody ? "Admin check success:" : "User is not an admin:", aBody || aRes.statusCode); callback(null, body); }); });
内容的提问来源于stack exchange,提问作者D. Gal

