为django-rest-framework的Password Validator自定义HTTP状态码

葛修筠
2023-12-01

背景:

在开发的时候,安卓前端表示拆包会很麻烦,最好是有http状态码,而不是在包里提供错误信息。而django password validator报错时默认的http状态码都是400,错误信息都是在包里提示的。

解决方案:

根据文档,发现rest framework可以通过APIView自定义报错,于是我编写了如下的代码:

from rest_framework.exceptions import APIException

class MinimumLengthError(APIException):
    status_code = 401
    default_detail = '密码长度太短,至少8位!'
    default_code = 'minimum_length_error'

class UserAttributeSimilarityError(APIException):
    status_code = 402
    default_detail = '密码和用户信息重复度太高!'
    default_code = 'user_attribute_similarity_error'

class CommonPassworError(APIException):
    status_code = 403
    default_detail = '密码太常见!'
    default_code = 'common_password_error'

class NumericPasswordError(APIException):
    status_code = 404
    default_detail = '密码不能是纯数字的!'
    default_code = 'numeric_password_error'

class PasswordResetTokenError(APIException):
    status_code= 405
    default_detail = 'Token错误'
    default_code = 'password_reset_token_error'

其中APIException时可以自定义的报错。其参数status code就是我们想要的http状态码。

接着我们修改django的password validator。将每一个validator的报错替换为我们新修改的异常。

# 修改后
class NumericPasswordValidator:
    """
    Validate whether the password is alphanumeric.
    """
    def validate(self, password, user=None):
        if password.isdigit():
            # 在这里修改
            raise errors.NumericPasswordError

    def get_help_text(self):
        return _("Your password can't be entirely numeric.")

这样我们就有可以反馈自定义状态码的password validator了。

接着我们可以将django默认的validator修改为我们自己定义的validator,这个可以在settings里设置。这方面教程很多,就不详细展开了。

另一种方式是,为了在用DRF(Django Rest Framework)的同时可以让MTV模式也可以像原来一样报错,有错误提示,我们可以修改django的validators.py这个文件。

首先我们复制一份到项目里,然后修改validate_password函数:

def validate_password(password, user=None, password_validators=None):
    """
    Validate whether the password meets all validator requirements.

    If the password is valid, return ``None``.
    If the password is invalid, raise ValidationError with all error messages.
    """
    

    if password_validators is None:
        password_validators = get_default_password_validators()
    for validator in password_validators:
        try:
            validator.validate(password, user)
        except APIException as error:
            raise error
    

这样就会只报一个异常,而不是像原来那样报很多error。

接着需要修改初始化validator的函数:

@functools.lru_cache(maxsize=None)
def get_default_password_validators():
    return get_password_validators(settings.REST_PASSWORD_VALIDATORS)

# settings里的REST_PASSWORD_VALIDATORS是我们自己建的validator

这样,我们就大功告成了。

之后在authenticate的时候我们就可以用自己的validators.py里面的validate_password函数。

(新人第一次写博客,请多多包涵)

 类似资料: