如何在Node.js的mongoose中的一个findOne函数内调用另一个findOne函数?

huangapple go评论81阅读模式
英文:

How to call a findOne function within another findOne function Node.js mongoose

问题

//FlashCardSchema
const flashcardSchema = new Schema({
    deckID: { type: Schema.Types.ObjectId, required: true },
    side1: { type: String, required: true },
    side2: { type: String, required: true }
}, { collection: "Flashcards" })

//FlashCardSetSchema
const flashcardDeckSchema = new Schema({
    collectionID: { type: Schema.Types.ObjectId, required: true },
    setTitle: { type: String, required: true },
    flashcards: [flashcardSchema]
}, { collection: "Decks" });

//FlashCard DB Schemas
const flashcardCollectionSchema = new Schema({
    userID: {
        type: Schema.Types.ObjectId,
        required: true
    },
    decks: [flashcardDeckSchema]
}, { collection: "FlashcardCollection" });

// 登录代码
exports.login = async (req, res, next) => {
    const email = req.body.email;
    const password = req.body.password;
    let loadedUser;
    let loadedFlashcardCollection;

    try {
        const user = await User.findOne({ email: email });
        if (!user) {
            const error = new Error('此电子邮件对应的用户不存在');
            error.statusCode = 401;
            throw error;
        }
        loadedUser = user;

        const isEqual = await bcrypt.compare(password, user.password);
        if (!isEqual) {
            const error = new Error("密码错误");
            error.statusCode = 401;
            throw error;
        }

        loadedFlashcardCollection = await getCollectionId(loadedUser._id);

        const token = jwt.sign(
            { 
                email: loadedUser.email, 
                userId: loadedUser._id.toString(), 
                loadedFlashcardCollection: loadedFlashcardCollection.toString() 
            }, 
            'JWTSECRETTOKEN', 
            { expiresIn: '2h' }
        );

        res.status(200).json({ 
            token: token, 
            userId: loadedUser._id.toString(), 
            collectionID: loadedFlashcardCollection.toString() 
        });
    } catch (err) {
        if (!err.statusCode) {
            err.statusCode = 500;
        }
        next(err);
    }
}

async function getCollectionId(loadedUserID) {
    const collection = await flashcardCollection.findOne({ userID: loadedUserID });
    if (!collection) {
        const error = new Error('未找到集合');
        error.statusCode = 401;
        throw error;
    }
    return collection._id;
}
英文:

Currently I am trying to write RESTapi for a website with users and flashcards. I am using a MERN stack in order to build this. The mongodb structure is the following:

//FlashCardSchema
const flashcardSchema = new Schema({
deckID: { type: Schema.Types.ObjectId, required: true },
side1: { type: String, required: true },
side2: { type: String, required: true }
}, { collection: "Flashcards" })
//FlashCardSetSchema
const flashcardDeckSchema = new Schema({
collectionID: { type: Schema.Types.ObjectId, required: true },
setTitle: { type: String, required: true },
flashcards: [flashcardSchema]
}, { collection: "Decks" });
//FlashCard DB Schemas
const flashcardCollectionSchema = new Schema({
userID: {
type: Schema.Types.ObjectId,
required: true
},
decks: [flashcardDeckSchema]
}, { collection: "FlashcardCollection" });

As you can see a flashcard collection is also connected to a user collection through its auto generated ID by mongodb. This means each user has only one "flashcardcollection" which is generated the same time a user registers.

Currently I am working on the login page and since this project is working with RESTApis I am using JWT tokens for login/authorization, and within these tokens I am passing on information such as UserID.

What I am trying to do currently is when someone tries to login, it verifies them, and finds the flashcardcollection assigned to them based on their userID. I ran into a problem of trying to call a findOne function within another findOne function and therefore I am not progressing.

Currently the login code looks as the following:

//Request email, password -> find user with email -> if no user throw error -> get password -> decrypt and compare -> if false throw error -> if true generate JWT token
//JWT secret == JWTSECRETKEY , Expires in 1 hour
exports.login = async (req, res, next) => {
const email = req.body.email;
const password = req.body.password;
let loadedUser;
let loadedFlashcardCollection;
User.findOne({ email: email }).then(user => {
if (!user) {
const error = new Error('A user with this email does not exist');
error.statusCode = 401;
throw error;
}
loadedUser = user;
return bcrypt.compare(password, user.password);
})
.then(isEqual => {
if (!isEqual) {
const error = new Error("Wrong Password");
error.statusCode = 401;
throw error;
}
loadedFlashcardCollection = getCollectionId(loadedUser._id);
//Error caused by trying to insert flashcardID into  JWT token and not being able to get a respons
const token = jwt.sign({ email: loadedUser.email, userId: loadedUser._id.toString(), loadedFlashcardCollection: loadedFlashcardCollection.toString() }, 'JWTSECRETTOKEN', { expiresIn: '2h' });
res.status(200).json({ token: token, userId: loadedUser._id.toString(), collectionID: loadedFlashcardCollection.toString() })
})
.catch(err => {
if (!err.statusCode) {
err.statusCode = 500;
}
next(err);
})
}
//function to find userID async way since it was needed.
async function getCollectionId(loadedUserID) {
const collection = await flashcardCollection.findOne({ userID: loadedUserID });
if (!collection) {
error.statusCode = 401;
throw new Error(`No collection found.`);
}
return collection._id;
}

and this returns the following when I try to log in through a POSTMAN

api:
{

"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImV4YW1wbGUyQGdtYWlsLmNvbSIsInVzZXJJZCI6IjY0NmRkZDZhMjJlMmZkYmJkYTFjYmU2OCIsImxvYWRlZEZsYXNoY2FyZENvbGxlY3Rpb24iOiJbb2JqZWN0IFByb21pc2VdIiwiaWF0IjoxNjg0OTUwNTk4LCJleHAiOjE2ODQ5NTc3OTh9.CRkMZ1m-CfZX7RLB-YXo6y9Ur_7tK4C1SPvCCQb-4ZE",

"userId": "646ddd6a22e2fdbbda1cbe68",

"collectionID": "[object Promise]"
}

can you guys help me figure out how to pass on the collectionID of the user in the same JWT token

I have tried creating async await functions but since i am new to NodeJs i am having a problem understanding how they work, therefore so far this was my closest attempt to success(at least its not throwing an error)

答案1

得分: 0

"Since getCollectionId is an asynchronous function and it returns a promise from the database, you need to wait for the response. It will look something like this,

.then(async (isEqual) => {

// your other code

loadedFlashcardCollection = await getCollectionId(loadedUser._id);

})
```"

<details>
<summary>英文:</summary>

Since getCollectionId is asynchronous function and it return a promise from db, you need to wait for the response so it will be something like this,



.then( **async** (isEqual) =&gt; {

// your other code

 loadedFlashcardCollection = **await** getCollectionId(loadedUser._id);

})



</details>



# 答案2
**得分**: 0

Firstly, 在初学者级别上,你需要理解 **async/await** 和 **.then/catch**,[Async Await vs then/catch][1],[Promise][2],这将为你提供一些见解。

当我们需要调用并等待 **async** 函数或 **Promise** 时,我们使用 **await**。
在你的情况下,当你调用 **getCollectionId** 时,如果没有使用 **await**,在你的 **getCollectionId** 中,函数会工作,但是你的 **getCollectionId** 不会等待函数完全完成。
你得到一个 Promise 对象,因为 **async** 函数返回 **Promise**,因为没有 await,**getCollectionId** 不等待 **settled** 的 Promise,所以它处于挂起状态,这是一个初始状态,既不履行也不拒绝。

所以,在 .then() 内部:

```javascript
async(isEqual) => {
  if (!isEqual) {
    const error = new Error("Wrong Password");
    error.statusCode = 401;
    throw error;
  }
  loadedFlashcardCollection = await getCollectionId(loadedUser._id);

我们在 .then() 内部的回调函数中使用了 async,因为 await 只能在 async 函数内部使用。

英文:

Firstly , On a beginner level , you need to understand the async/await and .then/catch, Async Await vs then/catch , Promise , this will give you bit insight .

We use await when we need to call and wait for async function or Promise .
In your case when, you call getCollectionId without await ,inside your getCollectionId, your function will work but your getCollectionId will not wait for the function to completely finishes.
You are getting a promise object because async functions returns Promise, since without await, getCollectionId is not waiting to settled Promise ,so ,its in pending state,which is an initial state, neither fulfilled nor rejected.


So ,inside .then()

async(isEqual) =&gt; {
if (!isEqual) {
const error = new Error(&quot;Wrong Password&quot;);
error.statusCode = 401;
throw error;
}
loadedFlashcardCollection = await getCollectionId(loadedUser._id);

we used async in the callback function inside .then(), because await can only be used inside async function.

huangapple
  • 本文由 发表于 2023年5月25日 02:07:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/76326339.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定