如何在 DRF 中强制执行 POST 幂等性?

mga*_*lgs 4 python django-rest-framework

我有一个使用 Django Rest Framework 的 API,我想防止重复POST请求(本着Post Once Exactly (POE)的精神)。特别是,我试图处理的场景是:

  1. 客户端发送 HTTP POST 以创建对象。
  2. API 后端创建对象并将其提交到数据库。
  3. 客户端失去网络连接。
  4. API 后端尝试发回成功响应,但由于客户端丢失网络而无法这样做。
  5. 客户端永远不会得到“成功”响应,因此假设请求失败。客户端重试请求,创建一个重复的对象。

邮件列表上对此进行了一些讨论,但没有具体化代码。人们现在如何解决这个问题?

mga*_*lgs 8

我通过添加对X-Idempotency-Key可由客户端设置的http 标头的支持来解决此问题。然后,我使用自定义权限类检查非幂等请求,该类检查最近是否已看到幂等键(在缓存中):

class IsIdempotent(permissions.BasePermission):
    message = 'Duplicate request detected.'

    def has_permission(self, request, view):
        if request.method != 'POST':
            return True
        ival = request.META.get('HTTP_X_IDEMPOTENCY_KEY')
        if ival is None:
            return True
        ival = ival[:128]
        key = 'idemp-{}-{}'.format(request.user.pk, ival)
        is_idempotent = bool(cache.add(key, 'yes',
                                       settings.IDEMPOTENCY_TIMEOUT))
        if not is_idempotent:
            logger.info(u'Duplicate request (non-idempotent): %s', key)
        return is_idempotent
Run Code Online (Sandbox Code Playgroud)

我可以像这样添加到我的观点中:

class MyViewSet(mixins.RetrieveModelMixin,
                mixins.UpdateModelMixin,
                mixins.ListModelMixin,
                viewsets.GenericViewSet):
    permission_classes = [permissions.IsAuthenticated,
                          IsIdempotent]
Run Code Online (Sandbox Code Playgroud)