Thinkster.io 链接https://thinkster.io/tutorials/node-json-api/setting-up-users-and-authentication
我在发送到我的应用 API 的任何邮递员请求中收到 404。
这是我从邮递员那里得到的错误"评估测试脚本时出错:JSONError:意外的令牌'<',1:1 ^"但它真的是 404,我在我的控制台中得到这个"POST/users 404 0.396 ms - 145">
邮递员截图
我认为这可能是我的路由,但我再次浏览了教程,我现在认为我在 Postman 中做错了什么。我通过 Thinksters Postman 演练和我在他们的服务器上的所有测试工作进行了工作,但不知道我错过了什么。
应用.js
var fs = require('fs'),
http = require('http'),
path = require('path'),
methods = require('methods'),
express = require('express'),
bodyParser = require('body-parser'),
session = require('express-session'),
cors = require('cors'),
passport = require('passport'),
errorhandler = require('errorhandler'),
mongoose = require('mongoose');
var isProduction = process.env.NODE_ENV === 'production';
// Create global app object
var app = express();
app.use(cors());
// Normal express config defaults
app.use(require('morgan')('dev'));
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(require('method-override')());
app.use(express.static(__dirname + '/public'));
app.use(session({ secret: 'conduit', cookie: { maxAge: 60000 }, resave: false, saveUninitialized: false }));
if (!isProduction) {
app.use(errorhandler());
}
if(isProduction){
mongoose.connect(process.env.MONGODB_URI);
} else {
mongoose.connect('mongodb://localhost/conduit');
mongoose.set('debug', true);
}
require('./models/User');//Be sure to include models before routes so that our routes will be able to use our models. this allows us to use the user model with auth
require('./config/passport');//username and password auth middleware for login endpoint /login
app.use(require('./routes'));
/// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
/// error handlers
// development error handler
// will print stacktrace
if (!isProduction) {
app.use(function(err, req, res, next) {
console.log(err.stack);
res.status(err.status || 500);
res.json({'errors': {
message: err.message,
error: err
}});
});
}
// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.json({'errors': {
message: err.message,
error: {}
}});
});
// finally, let's start our server...
var server = app.listen( process.env.PORT || 3000, function(){
console.log('Listening on port ' + server.address().port);
});
路由/用户.js
var mongoose = require('mongoose');
var router = require('express').Router();
var passport = require('passport');
var User = mongoose.model('User');
var auth = require('../auth');
//We rely on Mongoose validations to ensure that users are created with a username or password. When user.save() is called, a promise is returned for us to handle. If the promise is resolved, that means the user was saved successfully, and we return the user's auth JSON. If the promise gets rejected, we use .catch() to pass the error to our error handler.
router.post('/users', function(req, res, next){
var user = new User();
user.username = req.body.user.username;
user.email = req.body.user.email;
user.setPassword(req.body.user.password);
user.save().then(function(){
return res.json({user: user.toAuthJSON()});
}).catch(next);
});
//First, we're checking to make sure an email and password were provided by the front-end and respond with a 422 status code if they're not. Then, we pass the incoming request to passport.authenticate and specify that we want to use the local strategy we made previously (in config/passport.js). Since we're using JWTs for authentication and not using sessions, we also specify {session: false} to prevent Passport from serializing the user into the session. Finally, we define a callback for the passport strategy (this gets used as the done function in our LocalStrategy in config/passport.js) that will respond to the client based off if the authentication was successful or not.
router.post('/users/login', function(req, res, next){
if(!req.body.user.email){
return res.status(422).json({errors: {email: "can't be blank"}});
}
if(!req.body.user.password){
return res.status(422).json({errors: {password: "can't be blank"}});
}
//While we can implement this functionality without the use of Passport in our route, an advantage to using Passport is that it gives us the ability to add other authentication strategies such as OAuth in the future.
passport.authenticate('local', {session: false}, function(err, user, info){
if(err){ return next(err); }
if(user){
user.token = user.generateJWT();
return res.json({user: user.toAuthJSON()});
} else {
return res.status(422).json(info);
}
})(req, res, next);
});
router.get('/user', auth.required, function(req, res, next){
User.findById(req.payload.id).then(function(user){
if(!user){ return res.sendStatus(401); }
return res.json({user: user.toAuthJSON()});
}).catch(next);
});
router.put('/user', auth.required, function(req, res, next){
User.findById(req.payload.id).then(function(user){
if(!user){ return res.sendStatus(401); }
// only update fields that were actually passed...
if(typeof req.body.user.username !== 'undefined'){
user.username = req.body.user.username;
}
if(typeof req.body.user.email !== 'undefined'){
user.email = req.body.user.email;
}
if(typeof req.body.user.bio !== 'undefined'){
user.bio = req.body.user.bio;
}
if(typeof req.body.user.image !== 'undefined'){
user.image = req.body.user.image;
}
if(typeof req.body.user.password !== 'undefined'){
user.setPassword(req.body.user.password);
}
return user.save().then(function(){
return res.json({user: user.toAuthJSON()});
});
}).catch(next);
});
module.exports = router;
路由/索引.js
var router = require('express').Router();
router.use('/api', require('./api'));
module.exports = router;
路由/API/索引.js
var router = require('express').Router();
router.use('/', require('./users'));
//When a middleware is defined with four arguments, it will be treated as an error handler (the first argument is always the error object). This error handler sits after all of our API routes and is used for catching ValidationErrors thrown by mongoose. The error handler then parses the error into something our front-end can understand, and then responds with a 422 status code.
router.use(function(err, req, res, next){
if(err.name === 'ValidationError'){
return res.status(422).json({
errors: Object.keys(err.errors).reduce(function(errors, key){
errors[key] = err.errors[key].message;
return errors;
}, {})
});
}
return next(err);
});
module.exports = router;
型号/用户.js
var mongoose = require('mongoose'); //handles models/schema for mongodb i/o
var uniqueValidator = require('mongoose-unique-validator');//builting in unique combo validation
var crypto = require('crypto'); //library that generates and validates hashes
var jwt = require('jsonwebtoken');
var secret = require('../config').secret;//We need a secret to sign and validate JWT's. This secret should be a random string that is remembered for your application; it's essentially the password to your JWT's. In config/index.js there's a secret value which is set to "secret" in development and reads from an environment variable in production.
var UserSchema = new mongoose.Schema({
username: {type: String, lowercase: true, unique: true, required: [true, "can't be blank"], match: [/^[a-zA-Z0-9]+$/, 'is invalid'], index: true},
email: {type: String, lowercase: true, unique: true, required: [true, "can't be blank"], match: [/S+@S+.S+/, 'is invalid'], index: true},
image: String,
hash: String,
salt: String
}, {timestamps: true});
//plugging in a validator defind above, unique:true must be used for validation
UserSchema.plugin(uniqueValidator, {message: 'is already taken.'});
//To see if a password is valid for a particular user, we need to run the pbkdf2 with the same number of iterations and key length as our setPassword function with the salt of the user; then we need to check to see if the resulting hash matches the one that's stored in the database.
UserSchema.methods.setPassword = function(password){
this.salt = crypto.randomBytes(16).toString('hex');
this.hash = crypto.pbkdf2Sync(password, this.salt, 10000, 512, 'sha512').toString('hex');
};
//Next, we'll need a method on our model for generating a JWT (JSON Web Token). JWT's are the tokens that will be passed to the front-end that will be used for authentication. The JWT contains a payload (assertions) that is signed by the back-end, so the payload can be read by both the front-end and back-end, but can only be validated by the back-end.
UserSchema.methods.validPassword = function(password) {
var hash = crypto.pbkdf2Sync(password, this.salt, 10000, 512, 'sha512').toString('hex');
return this.hash === hash;
};
//Lastly, we'll need a method on the user model to get the JSON representation of the user that will be passed to the front-end during authentication. This JSON format should only be returned to that specific user since it contains sensitive information like the JWT.
UserSchema.methods.generateJWT = function() {
var today = new Date();
var exp = new Date(today);
exp.setDate(today.getDate() + 60);
return jwt.sign({
id: this._id,
username: this.username,
exp: parseInt(exp.getTime() / 1000),
}, secret);
};
UserSchema.methods.toAuthJSON = function(){
return {
username: this.username,
email: this.email,
token: this.generateJWT(),
bio: this.bio,
image: this.image
};
};
mongoose.model('User', UserSchema);
看起来/user 路由需要身份验证。我的猜测是您的邮递员脚本不包含包含 JWT 的必需身份验证标头