关于restfull api 接口问题。



 class User(db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(64), unique=True, index=True)
    username = db.Column(db.String(64), unique=True, index=True)
    role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
    password_hash = db.Column(db.String(128))
    confirmed = db.Column(db.Boolean, default=False)
    name = db.Column(db.String(64))
    location = db.Column(db.String(64))
    about_me = db.Column(db.Text())
    member_since = db.Column(db.DateTime(), default=datetime.utcnow)
    last_seen = db.Column(db.DateTime(), default=datetime.utcnow)
    avatar_hash = db.Column(db.String(32))
    posts = db.relationship('Post', backref='author', lazy='dynamic')
    comments = db.relationship('Comment', backref='author', lazy='dynamic')


    def to_json(self):
        json_user = {
            'url': url_for('api.get_post', id=self.id, _external=True),
            'username': self.username,
            'member_since': self.member_since,
            'last_seen': self.last_seen,
            'posts': url_for('api.get_user_posts', id=self.id, _external=True),
            'post_count': self.posts.count()
        }
        return json_user

class Post(db.Model):
    __tablename__ = 'posts'
    id = db.Column(db.Integer, primary_key=True)
    body = db.Column(db.Text)
    body_html = db.Column(db.Text)
    timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
    author_id = db.Column(db.Integer, db.ForeignKey('users.id'))
    comments = db.relationship('Comment', backref='post', lazy='dynamic')

    def to_json(self):
        json_post = {
            'url': url_for('api.get_post', id=self.id, _external=True),
            'body': self.body,
            'body_html': self.body_html,
            'timestamp': self.timestamp,
            'author': url_for('api.get_user', id=self.author_id,
                              _external=True),
            'comments': url_for('api.get_post_comments', id=self.id,
                                _external=True),
            'comment_count': self.comments.count()
        }
        return json_post

2个类的定义如上,然后,假设现在需要获取设计两个接口:

  • 获取某个文章详细信息的接口。
  • 获取某个人发表的文章详细信息。
    我看到很多教程都是类似这样设计的:

 @api.route('/posts/<int:id>')
def get_post(id):
    post = Post.query.get_or_404(id)
    return jsonify(post.to_json())

@api.route('/users/<int:id>/posts/')
def get_user_posts(id):
    user = User.query.get_or_404(id)
    page = request.args.get('page', 1, type=int)
    pagination = user.posts.order_by(Post.timestamp.desc()).paginate(
        page, per_page=current_app.config['FLASKY_POSTS_PER_PAGE'],
        error_out=False)
    posts = pagination.items
    prev = None
    if pagination.has_prev:
        prev = url_for('api.get_posts', page=page-1, _external=True)
    next = None
    if pagination.has_next:
        next = url_for('api.get_posts', page=page+1, _external=True)
    return jsonify({
        'posts': [post.to_json() for post in posts],
        'prev': prev,
        'next': next,
        'count': pagination.total
    })

对于接口返回的数据我有几个疑问:
1:为什么很多详细信息都是返回URL而不是实体。
比如:Class User类的to_json()方法为什么不像这样设计?


 def to_json(self):
    json_user = {
        'url': url_for('api.get_post', id=self.id, _external=True),
        'username': self.username,
        'member_since': self.member_since,
        'last_seen': self.last_seen,
        'posts': [post.to_json() for post in self.posts],
        'post_count': self.posts.count()
    }
    return json_user

2:如果要返回实体信息要怎么操作好?
3:我在github中看到很多flsk开源的项目,里面序列化或者反序列化都是用类似上面to_json, to_dict实现的,包括很多教程都是这样设计的。这种方法,个人觉得对于Model有几十个属性就感觉很无力,有什么好的办法?

restful 序列化 python flask

野良熊kuma 9 years, 4 months ago

按照自己对 Restful 的理解,接口是针对资源进行操作。 github 曾经设计 API 接口是针对单独的资源,而获取每个资源的接口,里面提供一些 url ,以便可以获取下一个,或者返回上一个资源。

1:为什么很多详细信息都是返回URL而不是实体。

设计两个接口,一个针对 user 这个资源获取详细信息,另外一个针对 post 这个资源获取详细信息。
其中在获取用户发布文章的详细信息的接口。涉及 user post 两个资源。根据查询条件是先找到 user ,再通过user去找post。因此可以看成是获取 user 这个资源,而 user 里涵盖了 post 资源,因此 post 字段换成获取 post 详细信息的接口,以便逻辑继续进行。

可以理解为一个资源就是一个表,一个接口只请求这个表的字段,至于外键字段的实体资源,则提供查询的 url 接口。抛开那些实体之间的 join 操作。

比如:Class User类的to_json()方法为什么不像这样设计?

看原来的设计,是获取 user 下某篇 post 的详细信息,关联了 post_id ,你改的那个是获取user的发布的 post 列表。

2:如果要返回实体信息要怎么操作好?

直接查询实体返回即可。

3 个人觉得对于 Model 有几十个属性就感觉很无力,有什么好的办法?
没看懂

其实尽管 restful 的提出了很久,也有所谓很多实践。不同的人理解不一样,也没有比较统一的说法。 github api 是一个比较好的例子。

实际开发中,几乎是不可能设计这样针对单个资源,资源之间通过 url 获取的接口的。因为先阶段来说,客户端工程师都希望一个接口搞定所有需要的数据,才不会管你什么资源啊,实体啊。通常喜欢减少网络的请求,哪怕传输的数据变多。

如果是自己想实现 restful 风格的程序,可以参考 github api 。在工作中,估计没有人会让你这么做,客户端要求什么,就写什么,不需要纠结。

kifen answered 9 years, 4 months ago

Your Answer