如何确保Django REST API中的幂等性?

3

假设我正在基于Django/DRF和PostgreSQL构建REST API。我希望所有GETPUTDELETE端点都遵循最佳实践并且是幂等的。我该如何确保/验证这一点呢?


Django/DRF在其中扮演了什么角色?请查看https://tools.ietf.org/html/rfc7231#section-8.1.3 - Tigran
我提到了Django/DRF,以防有任何处理幂等性的功能。 - planetp
不在核心功能中。您可以实现X-Idempotency-Key头和相应的中间件。 - Tigran
有一个库可以做到这一点,但不确定为什么它与Django 3.x.x不兼容。我已经要求作者提供信息,以便我可以进行PR。https://github.com/yoyowallet/django-idempotency-key - Kevin Parker
2个回答

3

虽然回答这个问题已经有点晚了,但我想为那些可能感兴趣的人回答一下。

默认情况下,GETDELETE是幂等的,因为唯一可能的响应状态要么是404,要么是发送数据,但对于PUT或者如果我们需要使任何POST请求成为幂等的,则不是这样。 对于PUT来说,最常见的方法是验证输入数据。确保传递了模型的所有字段(包括id),除非客户端收到了400 bad request,这样即使传递了id,也没有人可以向数据库添加不想要的记录。另一种100%确保一切都是幂等的方法是,我们可以使用缓存将请求正文和用户的id作为哈希存储到缓存服务器中足够长的时间,例如:

# in views.py
# for DRF
import hashlib

from django.core.cache import cache
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status

class IdempotentView(APIView):
     def put(self, req, pk, format=None):

         key = hashlib.blake2b(
                   f"{req.user.username}, {req.body},{pk}".encode("utf-8")
               ).hexdigest()
         is_cached = cache.get(key)

         if is_cached :
            return Response (req.body, status=status.HTTP_201_CREATED)
         
         # preform the view commands then:
         
         expiary  = 60*60*3 # 3 hours 
         cache.set(key, 'True' , exp_date)

         return Response (data, status=status.HTTP_201_CREATED)

1
我建议使用hashlib.blake2b这样的东西,而不是依赖于hash,因为这样可以减少碰撞的机会。 - suayip uzulmez

0

我相信以下帖子可以为这个主题提供一些启示:Django Rest Framework中的创建或更新(使用PUT)

引用:

class ResourceViewSet(viewsets.ModelViewSet):
    """
    This endpoint provides `create`, `retrieve`, `update` and `destroy` actions.
    """
    queryset = Resource.objects.all()
    serializer_class = ResourceSerializer

    def get_object(self):
        if self.request.method == 'PUT':
            resource = Resource.objects.filter(id=self.kwargs.get('pk')).first()
            if resource:
                return resource
            else:
                return Resource(id=self.kwargs.get('pk'))
        else:
            return super(ResourceViewSet, self).get_object()

但更优雅的解决方案还要检查权限 - 可以参考https://gist.github.com/tomchristie/a2ace4577eff2c603b1b


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接