Nodejs后端开发大本营

“真实世界”全栈开发-3.7-用户公开信息API

2018-02-07  本文已影响7人  桥头堡2015

在本教程的第二部分,我们定义的“用户公开信息”就是除去用户邮箱、密码及当前JWT令牌的其它信息:

{
  "profile": {
    "username": "jake",
    "bio": "I work at statefarm",
    "image": "https://static.productionready.io/images/smiley-cyrus.jpg",
    "following": false
  }
}

从数据库读取用户的公开信息

打开models/User.js,添加返回用户公开信息的方法:

// +++
UserSchema.methods.toProfileJSONFor = function () {
  return {
    username: this.username,
    bio: this.bio,
    image: this.image || 'https://static.productionready.io/images/smiley-cyrus.jpg',
    following:  false  // “关注”功能将在之后的章节里实现
  };
};
// +++

mongoose.model('User', UserSchema);

因为我们还没有实现“关注”用户的功能,所以following的值现在固定为false

如果用户没有设定头像(从数据库返回的this.image的值为null),我们也为image设定一个默认URL。

数据库那的任务解决了,现在我们来实现属于服务器的部分。就同之前的功能一样,这部分可以分为三大块:端点(已经在教程的第二部分规定好)、中间件(待写)以及路由(待写)。

负责用户公开信息的路由

我们先来定义负责用户公开信息的路由。它负责将端点映射到对应的中间件。

新建routes/api/profiles.js文件,写入:

const router = require('express').Router();
const mongoose = require('mongoose');
const User = mongoose.modle('User');
const auth = require('../auth');

module.exports = router;

接下来,按老规则,我们要把上面创建的router登记到API主路由上,好让后端应用使用。这个router的根URL将是/profiles,符合的端点在API设计里有三个,这一讲我们只实现其中“获取用户公开信息”的功能,把“关注用户”和“取消关注用户”的功能留给后面的章节。

打开routes/api/index.js,加入如下的一行代码:

router.use('/users', require('./users'));
// +++
router.use('/profiles', require('./profiles'));
// +++

路由对象创建好了,剩下要作的就是定义相关中间件并且把它们与端点联接起来。

我们需要在URL中指定用户名,好从数据库中获取该用户的公开信息。Express的参数中间件能够截取URL中的参数,以供后续的中间件使用。所谓参数中间件,就是在reqresnext之后还有其它参数,这些参数都是从URL中抽取出来的。

routes/api/profiles.js里添加:

// +++
router.param('username', (req, res, next, username) => {
  User.findOne({username: username}).then(user => {
    if (!user)
      return res.status(404).json({errors: {username: `no such username: ${username}`}});

    res.locals.profile = user;
    return next();
  }).catch(next);
});
// +++

module.exports = router

当该路由对象处理的URL模板里含有:username(冒号之后的与上面第一个字符串参数一致)时,上面的中间件就会利用该用户名从数据库获取用户对象,并存到res.locals.profile

实现并装配获取用户公开信息的中间件

routes/api/profiles.js继续加入:

// +++
router.get('/:username', auth.optional, (req, res, next) => {
  return res.json({profile: res.locals.profile.toProfileJSONFor()});
});
// +++

module.exports = router;

当后端收到方法为GET、URL形如/api/profiles/xxxxx的请求时,前面定义的参数中间件会首先被调用,抽取出用户名为xxxxx,并以此获取对应的用户对象(如果用户名存在的话),存在res.locals.profile里,然后才依次调用后面的两个中间件。最后的那个中间件调用该用户对象的toProfileJSONFor方法,把它的公开信息返回给前端。

为以后做准备

不过,做软件开发必须要多看一步。等到后面实现“关注、取消关注”的功能时,toProfileJSONFor还需要考察当前登录的用户为谁,以此来判断following的值到底是true还是false。这就意味着这个方法至少要接受一个参数(当前的用户对象)。

我们在models/User.js里做如下更改:

// ***
UserSchema.methods.toProfileJSONFor = function (user) {
// ***

我们这里仅仅改变该方法的签名,方法体的改变留到实现“关注、取消关注”的时候。

路由方面,如果获取用户公开信息的请求里含有令牌,Passport会把当前用户的ID解析成req.payload.id。有了ID(或者没有),我们可以用loadCurrentUser来载入当前用户,并把它传给更改后的profile.toProfileJSONFor。如果令牌不存在,就传入null,返回的following则为false

将上面的路由做如下更改:

// ***
const loadCurrentUser = require('./user').loadCurrentUser;

router.get('/:username', auth.optional, loadCurrentUser, (req, res, next) => {
  const user = req.locals.profile || null;
  return res.json({profile: req.locals.profile.toProfileJSONFor(user)});
});
// ***

module.exports = router;

上面的代码再次体现了Express中间件的灵活性。

到此为止,我们就实现了获取用户公开信息的API。

上一篇 下一篇

猜你喜欢

热点阅读