'错误:检测到循环依赖性'当我向模块添加唯一标识符时发生

时间:2018-06-08 08:04:45

标签: node.js debugging mongoose

我发现问题出在什么时候,问题发生了,但我不明白为什么会发生......

这是我的app.js,这里没有问题,我测试了10次以上:

'use strict';
const Joi = require('joi');
Joi.objectId = require('joi-objectid')(Joi);
const config = require('config');
const mongoose = require('mongoose');
const debug = require('debug')('app:api');
const express = require('express');
const path = require('path');
const morgan = require('morgan');
const bodyParser = require('body-parser');
const { isNotFound, errorHandlerDev, errorHandlerPro } = require('./middleware/handlers');

const index = require('./routes/index');
const auth = require('./routes/auth');
const users = require('./routes/users');

const app = express();

if(!config.get('jwtPrivateKey')) {
    console.log('KRİTİK HATA: JWT Private Key Tanımlanmadı!');
    process.exit(1);
}
if(!config.get('connectionString')) {
    console.log('KRİTİK HATA: Connection String Tanımlanmadı!');
    process.exit(1);
}
if(!process.env.USERNAME || !process.env.PASSWORD) {
    console.log('KRİTİK HATA: MongoDB Kullanıcı Bilgileri Eksik!');
    process.exit(1);
}

mongoose.connect(config.get('connectionString').replace(/({{USERNAME}}|{{PASSWORD}})/gi, match => { match = match.replace(/({|})/gi, symbol => ''); return process.env[match]; }))
    .then(() => debug('MongoDB > Uptime-Monitor Başarıyla Bağlandı!'))
    .catch(error => debug('MongoDB > Uptime-Monitor Bağlanırken Hata Oluştu! Hata Bilgisi:', error));

app.use(morgan('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));

app.use('/api/', index);
app.use('/api/auth', auth);
app.use('/api/users', users);

app.use(isNotFound);

if (app.get('env') === 'development') {
    app.use(errorHandlerDev);
}

app.use(errorHandlerPro);

app.set('port', process.env.PORT || 5000);

app.listen(app.get('port'), function () {
    debug(`Express Sunucusu Port ${app.get('port')} Üzerinde Çalışıyor!`);
});

这是我的用户模型文件user.js:

'use strict';
const config = require('config');
const jwt = require('jsonwebtoken');
const Joi = require('joi');
Joi.objectId = require('joi-objectid')(Joi);
const mongoose = require('mongoose');

var model = {};

// Required Data: username, email, phone, password
// Optional Data: name, lastname, tosAgreement, isAdmin
model.userSchema = new mongoose.Schema({
    name: {
        type: String,
        trim: true,
        minlength: [3, 'Adınızın Karakter Sayısı Uygun Değildir. Minimum 3, Maksimum 50 Adet Karakterden Oluşabilir.'],
        maxlength: [50, 'Adınızın Karakter Sayısı Uygun Değildir. Minimum 3, Maksimum 50 Adet Karakterden Oluşabilir.']
    },
    lastname: {
        type: String,
        trim: true,
        minlength: [3, 'Soyadınızın Karakter Sayısı Uygun Değildir. Minimum 3, Maksimum 50 Adet Karakterden Oluşabilir.'],
        maxlength: [50, 'Adınızın Karakter Sayısı Uygun Değildir. Minimum 3, Maksimum 50 Adet Karakterden Oluşabilir.']
    },
    username: {
        type: String,
        trim: true,
        minlength: [3, 'Kullanıcı Adınızın Karakter Sayısı Uygun Değildir. Minimum 3, Maksimum 30 Adet Karakterden Oluşabilir.'],
        maxlength: [20, 'Kullanıcı Adınızın Karakter Sayısı Uygun Değildir. Minimum 3, Maksimum 30 Adet Karakterden Oluşabilir.'],
        //unique: [true, 'İstenilen Kullanıcı Adı Daha Önce Kullanılmış.'],
        validate: {
            validator: function(v) {
                let re = /^\w+$/;
                return re.test(v);
            },
            message: 'Uygun Kullanıcı Adı Giriniz.'
        },
        match: [/^\w+$/, 'Uygun Kullanıcı Adı Giriniz.']
    },
    email: {
        type: String,
        trim: true,
        //unique: [true, 'İstenilen E-Posta Adresi Daha Önce Kullanılmış.'],
        lowercase: true,
        validate: {
            validator: function(v) {
                let re = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/;
                return re.test(v);
            },
            message: 'Uygun E-Posta Adresi Giriniz.'
        },
        match: [/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/, 'Uygun E-Posta Adresi Giriniz.']
    },
    phone: {
        type: Number,
        required: [true, 'Telefon Numaranızı Girmeniz Gerekmektedir.'],
        //unique: [true, 'İstenilen Telefon Numarası Daha Önce Kullanılmış.'],
        min: [10, 'Telefon Numaranızın Uzunluğu Uygun Değildir. 10 Haneden Oluşmalıdır.'],
    },
    password: {
        type: String,
        trim: true,
        required: [true, 'Şifrenizi Girmeniz Gerekmektedir.'],
        minlength: 5,
        maxlength: 1024
    },
    registerDate: {
        type: Date,
        default: Date.now
    },
    tosAgreement: {
        type: Boolean,
        default: true
    },
    isAdmin: {
        type: Boolean,
        required: true,
        default: false
    }
});

model.userSchema.methods.generateToken = function() {
    return jwt.sign({ _id: this._id, isAdmin: this.isAdmin}, config.get('jwtPrivateKey'));
};

model.User = mongoose.model('User', model.userSchema);

model.validate = user => {
    const Schema = {
        name: Joi.string().min(3).max(50),
        lastname: Joi.string().min(3).max(50),
        username: Joi.string().required().regex(/^\w+$/).min(3).max(20),
        email: Joi.string().required().min(3).email(),
        phone: Joi.number().required().min(10),
        password: Joi.string().required().min(5).max(255),
        tosAgreement: Joi.boolean().default(true),
        isAdmin: Joi.boolean().default(false)
    };

    return Joi.validate(user, Schema);
};

model.validateId = id => {
    return Joi.validate(id, Joi.objectId().required());
};

module.exports.model = model;

最后,我的users.js,api / users路由器,这里也没有问题:

'use strict';
const auth = require('./../middleware/auth');
const admin = require('./../middleware/admin');
const bcyript = require('bcrypt');
const _ = require('lodash');
const mongoose = require('mongoose');
const { userSchema, User, validate, validateId } = require('./../models/user');
const express = require('express');
const router = express.Router();

router.get('/', [auth, admin], async (req, res) => {
    const users = await User.find().select({ _id: 1, name: 1, email: 1, tosAgreement: 1}).sort({ name: 1 });
    if(!users) return res.status(200).send({'BİLGİ': 'Hiç Kullanıcı Bulunamadı.'});
    res.status(200).send(users);
});

router.get('/me', auth, async (req, res) => {
    const user = await User.findById(req.user._id).select('-password -phone');
    res.status(200).send(user);
});

router.get('/:id', [auth, admin], async (req, res) => {
    const user = await User.findById(req.params.id).select({ _id: 1, name: 1, email: 1, tosAgreement: 1, isAdmin: true});
    if(!user) return res.status(404).send({'HATA': 'Belirtilen ID\'ye Sahip Kullanıcı Bulunamadı.'});
    res.status(200).send(user);
});

router.post('/', /*[auth, admin],*/async (req, res) => {
    const { error } = validate(req.body);
    if(error) return res.status(400).send({'Hata': error.details[0].message});
    let isAnyUserWithThatEmail = await User.findOne({ email: req.body.email });
    if(isAnyUserWithThatEmail) return res.status(400).send({'Hata': 'Belirtilen E-Posta Adresi Kullanımda.'});
    let isAnyUserWithThatPhone = await User.findOne({ phone: req.body.phone });
    if(isAnyUserWithThatPhone) return res.status(400).send({'Hata': 'Belirtilen Telefon Numarası Kullanımda.'});
    const user = new User(req.body);
    const salt = await bcyript.genSalt(10);
    user.password = await bcyript.hash(user.password, salt);
    await user.save();
    const token = user.generateToken();
    res.header('x-auth-token', token).status(201).send(_.pick(user, ['_id', 'name', 'email', 'tosAgreement', 'isAdmin']));
});

router.put('/:id', auth, async (req, res) => {
    const errorId = validateId(req.params.id).error;
    if(errorId) return res.status(400).json({'Hata': errorId.details[0].message});
    const { error } = validate(req.body);
    if(error) return res.status(400).send({'Hata': error.details[0].message});
    const salt = await bcyript.genSalt(10);
    const user = await User.findByIdAndUpdate(req.params.id, {
        name: req.body.name,
        lastname: req.body.lastname,
        email: req.body.email,
        phone: req.body.phone,
        password: await bcyript.hash(req.body.password, salt),
        tosAgreement: req.body.tosAgreement,
        isAdmin: req.body.isAdmin
    }, { new: true, runValidators: true });
    if(!user) return res.status(404).send({'Hata': 'Belirtilen ID\'ye Sahip Kullanıcı Bulunamadı.'});
    res.status(200).send(_.pick(user, ['_id', 'name', 'email', 'tosAgreement', 'isAdmin']));
});

router.delete('/:id', [auth, admin], async (req, res) => {
    const { error } = validateId(req.params.id);
    if(error) return res.status(400).json({'Hata': error.details[0].message});
    const user = await User.findByIdAndRemove(req.params.id);
    if(!user) return res.status(404).send({'Hata': 'Belirtilen ID\'ye Sahip Kullanıcı Bulunamadı.'});
    res.status(200).send(_.pick(user, ['_id', 'name', 'email', 'tosAgreement', 'isAdmin']));
});

module.exports = router;

"错误:检测到循环依赖。"取消注释" unique:true"在我的模型(user.js)上。当我评论" unique:true"线条,它消失了。问题出在这里,但为什么呢?我需要使用唯一标识符。

//@@@ NO ERROR WHEN COMMENT OUT THESE 'unique' IDENTIFIERS @@@
...
username: {
    ...
    //unique: [true, 'İstenilen Kullanıcı Adı Daha Önce Kullanılmış.'],
    ...
},
email: {
    ...
    //unique: [true, 'İstenilen E-Posta Adresi Daha Önce Kullanılmış.'],
    ...
},
phone: {
    ...
    //unique: [true, 'İstenilen Kullanıcı Adı Daha Önce Kullanılmış.'],
    ...
}
...

1 个答案:

答案 0 :(得分:2)

我至少找到了解决方案, 我做了一些研究,发现了' index'字段或猫鼬的概念。我们需要在Schema上设置索引以使其唯一。

model.userSchema = new mongoose.Schema({
    ...
    username: {
        ...
        index: true,
        unique: [true, 'İstenilen Kullanıcı Adı Daha Önce Kullanılmış.'],
        ...
    },
    email: {
        ...
        index: true,
        unique: [true, 'İstenilen E-Posta Adresi Daha Önce Kullanılmış.'],
        ...
    },
    phone: {
        ...
        index: true,
        unique: [true, 'İstenilen Kullanıcı Adı Daha Önce Kullanılmış.'],
        ...
    }
    ...
});

model.userSchema.set('autoIndex', false); // REQUIRED FOR UNIQUE INDEX AND INDEX IDENTIFIERS