• 对于接口开发的一些思考
  • 发布于 2个月前
  • 146 热度
    0 评论
flask_sqlalchemy 扩展包对 ORM 框架 SQLAlchemy 进行了简单封装,对 Query对象封装后支持分页操作,就是 flask_sqlalchemy.BaseQuery.paginate 方法,传入页数和每页大小就会返现对应的 Pagination 对象。例如:(代码可以左右滑)
pagination = Article.query.join(Account) \
        .filter(Article.status == 1) \
        .order_by(Article.published_at.desc()) \
        .paginate(page, per_page)
Pagination 对象里面有数据,有数据总条数。这样比自己实现分页能节省几行代码,然后把代码重构成用 paginate 后,遇到个诡异的问题。发现访问带分页参数的 URL 报 404 了,一开始以为是我输入的 URL 不正确,然后打印 url_map 里面值,对比没有毛病。 我又尝试 把 paginate 去掉,还原成原来的 limit 和 offset 方法来控制分页,这样又恢复正常了。这样我确定是这个 paginate 方法搞的鬼,然后就找到里面的源码,发现该方法有个error_out 参数
    def paginate(self, page=None, per_page=None, error_out=True, max_per_page=None):
        """Returns ``per_page`` items from page ``page``.

        If ``page`` or ``per_page`` are ``None``, they will be retrieved from
        the request query. If ``max_per_page`` is specified, ``per_page`` will
        be limited to that value. If there is no request or they aren't in the
        query, they default to 1 and 20 respectively.
        When ``error_out`` is ``True`` (default), the following rules will
        cause a 404 response:
        * No items are found and ``page`` is not 1.
        * ``page`` is less than 1, or ``per_page`` is negative.
        * ``page`` or ``per_page`` are not ints.

        When ``error_out`` is ``False``, ``page`` and ``per_page`` default to
        1 and 20 respectively.
        Returns a :class:`Pagination` object.
        """
        if request:
            if page is None:
                try:
                    page = int(request.args.get('page', 1))
                except (TypeError, ValueError):
                    if error_out:
                        abort(404)
                    page = 1
            if per_page is None:
                try:
                    per_page = int(request.args.get('per_page', 20))
                except (TypeError, ValueError):
                    if error_out:
                        abort(404)

                    per_page = 20
        else:
            if page is None:
                page = 1
            if per_page is None:
                per_page = 20
        if max_per_page is not None:
            per_page = min(per_page, max_per_page)
        if page < 1:
            if error_out:
                abort(404)
            else:
                page = 1
        if per_page < 0:
            if error_out:
                abort(404)
            else:
                per_page = 20
        items = self.limit(per_page).offset((page - 1) * per_page).all()
        if not items and page != 1 and error_out:
            abort(404)
error_out 默认值为 True,这段代码的意思就是任何异常行为都会导致 404 响应,比如你传入的page参数不是数字就报 404,如果小于1也报 404, 没有数据时也会报 404。 我在是写 API 接口, 你给我报 404,太特么不厚道了。

作为一个第三方库,默认给开发者做决定个人认为并不妥当,因为你的本意可能是 dont make me think, 反而让我陷入了沉思,因为我不知道为什么报 404,看不到任何堆栈日志,更合理的方式应该是用异常的方式告知开发者。这样我就知道怎么去修改参数来返回期望的结果。其实,这跟产品经理设计产品是一样的道理,对最终用户我们有必要隐藏所有的技术细节,任何错误日志都不应该展示给用户,因为用户看不懂,也不需要让它看懂。而错误日志对开发者来说却至关重要,如果把这些日志也给我隐藏了,我不得不去深入源码里面找原因。

作为一个产品经理,同理心是非常重要,做为开发者也是如此,当你在给其他人提供接口的时候,是否能让人舒服一样非常重要
用户评论