Returning custom message when a permission is deni

2019-06-18 04:33发布

问题:

Django REST Framework has an excellent piece of documentation about permissions. I've been able to use pre-made permission classes and also built my own.

However, there are some API methods in which a "Permission denied" generic message is not very informative for the user. For example, if the user is authenticated but the account has expired, it would be nice to let the user know that his account is expired and not just a permission denied error.

When building custom permission classes, you either return True or False - according to the documentation. But I would like, as said above, to show a more informative message to the user. How to accomplish this?

回答1:

Since DRF 3.2.0, You only have to add a message attribute :

from rest_framework import permissions

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

    def has_permission(self, request, view): 

See from DRF documentation: http://www.django-rest-framework.org/api-guide/permissions/#custom-permissions



回答2:

By default, it is handled by default exception handler, and it is raising a standard message - https://github.com/tomchristie/django-rest-framework/blob/2eb9107b875972e442ed73eef0e653fd4480d873/rest_framework/views.py#L82

But, you can set own EXCEPTION_HANDLER in settings of DRF, and handle PermissionDenied exception to return message you want.

See description at http://www.django-rest-framework.org/api-guide/settings/



回答3:

From DRF

you can simply add message attribute.

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

It will return a dict with key detail, something like this:

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

But what if you want for example that the dict key not to be detail but errors for example, it will be the same how return errors DRF.

We can set message attribute not to string but to dict, something like this:

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

In this case the error will be:

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


回答4:

I faced the same problem using DRF 3.9.4. As a workaround I defined just a simple message property in the custom permission class and it works. You can also use getattr with the same result I guess.

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)