Возврат пользовательского сообщения, когда в DRF отказано в разрешении

Django REST Framework имеет отличную документацию о разрешениях. Я смог использовать заранее подготовленные классы разрешений, а также создал свои собственные.

Однако существуют некоторые методы API, в которых общее сообщение "Отказано в доступе" не очень информативно для пользователя. Например, если пользователь прошел проверку подлинности, но срок действия учетной записи истек, было бы неплохо сообщить пользователю, что срок действия его учетной записи истек, а не просто ошибка отказа в разрешении.

При создании пользовательских классов разрешений вы либо возвращаете True или же False - согласно документации. Но я бы хотел, как сказано выше, показать пользователю более информативное сообщение. Как это сделать?

8 ответов

Решение

Начиная с DRF 3.2.0, вам нужно только добавить атрибут сообщения:

from rest_framework import permissions

class CustomerAccessPermission(permissions.BasePermission):
    message = 'Adding customers not allowed.'

    def has_permission(self, request, view): 

См. Документацию DRF: http://www.django-rest-framework.org/api-guide/permissions/

От DRF

Вы можете просто добавить message приписывать.

from rest_framework import permissions

class IsSuperUserPermission(permissions.BasePermission):
    message = 'User is not superuser'

    def has_permission(self, request, view):
        return self.request.user.is_superuser

Это вернет dict с ключом detail, что-то вроде этого:

{
    'detail': 'User is not superuser'
}

Но что, если вы хотите, например, что dict ключ не быть detail но errors например, будет так же, как return ошибки DRF.

Мы можем set message attribute не для string но dict, что-то вроде этого:

class IsSuperUserPermission(permissions.BasePermission):
    message = {'errors': ['User is not a superuser']}

    def has_permission(self, request, view):
        self.message['errors'].clear()
        return self.request.user.is_superuser

В этом случае ошибка будет:

{
    'errors': ['User is not a superuser']
}

Когда разрешение не предоставлено, я выдам исключение, которое пользовательский ответ. Работает на djangorestframewor(3.10.1) и django(2.2.3).

from rest_framework.permissions import BasePermission
from rest_framework.exceptions import APIException
from rest_framework import status


class IsLogin(BasePermission):
    """
    Allows access only to authenticated users.
    """

    def has_permission(self, request, view):
        if request.email:
            return True
        raise NeedLogin()


class NeedLogin(APIException):
    status_code = status.HTTP_403_FORBIDDEN
    default_detail = {'error': True, 'message': '请先登录'}
    default_code = 'not_authenticated'

Основываясь на ответе Айсеннусси:

from rest_framework import permissions
From django.utils import timezone

class CustomerAccessPermission(permissions.BasePermission):
    message = 'Adding customers not allowed.'

    def has_permission(self, request, view): 
        if request.user.has_expired:
            self.message = “Your account has expired.”
            return False
        elif request.user.has_access:
            return True
        else:
            return False

По умолчанию он обрабатывается обработчиком исключений по умолчанию и вызывает стандартное сообщение - https://github.com/tomchristie/django-rest-framework/blob/2eb9107b875972e442ed73eef0e653fd4480d873/rest_framework/views.py

Но вы можете установить собственный EXCEPTION_HANDLER в настройках DRF, и обрабатывать PermissionDenied исключение для возврата сообщения, которое вы хотите.

Смотрите описание на http://www.django-rest-framework.org/api-guide/settings/

Я столкнулся с той же проблемой при использовании DRF 3.9.4. В качестве обходного пути я определил простое свойство сообщения в пользовательском классе разрешений, и оно работает. Вы также можете использовать getattr с тем же результатом, я думаю.

class IPWhitelistPermission(permissions.BasePermission):

    def __init__(self):
        super(IPWhitelistPermission, self).__init__()
        self._client_ip = None

    def has_permission(self, request, view):
        ip = get_client_ip(request)
        ret = IPWhitelist.is_whitelisted(ip)

        if not ret:
            logger = logging.getLogger('access')
            logger.warn("Unauthorized access from IP %s" % ip)
            self._client_ip = ip
        return ret

    @property
    def message(self):
        return "This IP is not whitelisted [{}]".format(self._client_ip)

Простой метод использует класс PermissionDenied и вызывает исключение:

      from rest_framework.exceptions import PermissionDenied

...
if allowed:
   return True
else:
   # return False
   raise PermissionDenied('custom message')
...

Вы можете отправить более одного персонализированного сообщения, если хотите. Вы можете сделать это, используя GenericAPIException.

Шаг 1: Создайте файл permissions.py и напишите этот код.

      class Check_user_permission(permissions.BasePermission):
def has_permission(self, request, view):
    if request.method in permissions.SAFE_METHODS:
        return True
    else:
        response ={
            "success": "false",
            'message': "Post request is not allowed for user from admin group",
            "status_code":403,
        }
        raise GenericAPIException(detail=response, status_code=403)

Здесь, response — это ответ JSON, который вы хотите отправить.

Шаг 2: Перейдите к файлу view.py и добавьте класс Check_user_permissionв permission_classesсписок таким образом:

      class UserList(APIView):
    permission_classes = (IsAuthenticated, Check_user_permission)
    authentication_class = JSONWebTokenAuthentication
    ... 
    ...

Теперь, если вы перейдете к конечной точке и отправите запрос POST, вы получите этот ответ.

      {
"success": "false",
"message": "Post request is not allowed!",
"status_code": 403
}
Другие вопросы по тегам