返回介绍

将用户表示为 JSON 对象

发布于 2025-01-02 21:54:01 字数 3996 浏览 0 评论 0 收藏 0

实施 API 时要考虑的第一个方面是决定其资源表示形式。 我要实现一个用户类型的 API,因此我需要决定的是用户资源的表示形式。 经过一番头脑风暴,得出了以下 JSON 表示形式:

{
    "id": 123,
    "username": "susan",
    "password": "my-password",
    "email": "susan@example.com",
    "last_seen": "2017-10-20T15:04:27Z",
    "about_me": "Hello, my name is Susan!",
    "post_count": 7,
    "follower_count": 35,
    "followed_count": 21,
    "_links": {
        "self": "/api/users/123",
        "followers": "/api/users/123/followers",
        "followed": "/api/users/123/followed",
        "avatar": "https://www.gravatar.com/avatar/..."
    }
}

许多字段直接来自用户数据库模型。 password 字段的特殊之处在于,它仅在注册新用户时才会使用。 回顾 第五章 ,用户密码不存储在数据库中,只存储一个散列字符串,所以密码永远不会被返回。 email 字段也被专门处理,因为我不想公开用户的电子邮件地址。 只有当用户请求自己的条目时,才会返回 email 字段,但是当他们检索其他用户的条目时不会返回。 post_countfollower_countfollowed_count 字段是“虚拟”字段,它们在数据库字段中不存在,提供给客户端是为了方便。 这是一个很好的例子,它演示了资源表示不需要和服务器中资源的实际定义一致。

请注意 _links 部分,它实现了超媒体要求。 定义的链接包括指向当前资源的链接,用户的粉丝列表链接,用户关注的用户列表链接,最后是指向用户头像图像的链接。 将来,如果我决定向这个 API 添加用户动态,那么用户的动态列表链接也应包含在这里。

JSON 格式的一个好处是,它总是转换为 Python 字典或列表的表示形式。 Python 标准库中的 json 包负责 Python 数据结构和 JSON 之间的转换。因此,为了生成这些表示,我将在 User 模型中添加一个名为 to_dict() 的方法,该方法返回一个 Python 字典:

app/models.py :User 模型转换成表示。

from flask import url_for
# ...

class User(UserMixin, db.Model):
    # ...

    def to_dict(self, include_email=False):
        data = {
            'id': self.id,
            'username': self.username,
            'last_seen': self.last_seen.isoformat() + 'Z',
            'about_me': self.about_me,
            'post_count': self.posts.count(),
            'follower_count': self.followers.count(),
            'followed_count': self.followed.count(),
            '_links': {
                'self': url_for('api.get_user', id=self.id),
                'followers': url_for('api.get_followers', id=self.id),
                'followed': url_for('api.get_followed', id=self.id),
                'avatar': self.avatar(128)
            }
        }
        if include_email:
            data['email'] = self.email
        return data

该方法一目了然,只是简单地生成并返回用户表示的字典。正如我上面提到的那样, email 字段需要特殊处理,因为我只想在用户请求自己的数据时才包含电子邮件。 所以我使用 include_email 标志来确定该字段是否包含在表示中。

注意一下 last_seen 字段的生成。 对于日期和时间字段,我将使用 ISO 8601 格式,Python 的 datetime 对象可以通过 isoformat() 方法生成这样格式的字符串。 但是因为我使用的 datetime 对象的时区是 UTC,且但没有在其状态中记录时区,所以我需要在末尾添加 Z ,即 ISO 8601 的 UTC 时区代码。

最后,看看我如何实现超媒体链接。 对于指向应用其他路由的三个链接,我使用 url_for() 生成 URL(目前指向我在 app/api/users.py 中定义的占位符视图函数)。 头像链接是特殊的,因为它是应用外部的 Gravatar URL。 对于这个链接,我使用了与渲染网页中的头像的相同 avatar() 方法。

to_dict() 方法将用户对象转换为 Python 表示,以后会被转换为 JSON。 我还需要其反向处理的方法,即客户端在请求中传递用户表示,服务器需要解析并将其转换为 User 对象。 以下是实现从 Python 字典到 User 对象转换的 from_dict() 方法:

app/models.py :表示转换成 User 模型。

class User(UserMixin, db.Model):
    # ...

    def from_dict(self, data, new_user=False):
        for field in ['username', 'email', 'about_me']:
            if field in data:
                setattr(self, field, data[field])
        if new_user and 'password' in data:
            self.set_password(data['password'])

本处我决定使用循环来导入客户端可以设置的任何字段,即 usernameemailabout_me 。 对于每个字段,我检查它是否存在于 data 参数中,如果存在,我使用 Python 的 setattr() 在对象的相应属性中设置新值。

password 字段被视为特例,因为它不是对象中的字段。 new_user 参数确定了这是否是新的用户注册,这意味着 data 中包含 password 。 要在用户模型中设置密码,需要调用 set_password() 方法来创建密码哈希。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文