Thinkster.io教程部分:使用Postman中的Register请求创建一个帐户

时间:2019-03-25 13:22:46

标签: node.js api express postman

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 screenshot

我认为这可能是我的选择,但我又重新阅读了本教程,现在我想我在Postman中做错了什么。我通过Thinksters Postman进行了遍历,所有测试工作都在他们的服务器上进行,但是我不知道我所缺少的是什么。

app.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);
});

routes / user.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;

routes / index.js

var router = require('express').Router();

router.use('/api', require('./api'));

module.exports = router;

routes / api / index.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;

models / User.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);

1 个答案:

答案 0 :(得分:0)

/ user路由似乎需要身份验证。我的猜测是您的Postman脚本不包含必需的包含JWT的身份验证标头