我有一个API端点,允许用户注册帐户。我想为重复的用户名返回HTTP 409而不是400.
这是我的序列化器:
from django.contrib.auth.models import User
from rest_framework.serializers import ModelSerializer
class UserSerializer(ModelSerializer):
username = CharField()
def validate_username(self, value):
if User.objects.filter(username=value).exists():
raise NameDuplicationError()
return value
class NameDuplicationError(APIException):
status_code = status.HTTP_409_CONFLICT
default_detail = u'Duplicate Username'
触发错误时,响应为:{"detail":"Duplicate Username"}
。我意识到如果我将APIException子类化,则使用键detail
而不是username
。
我希望此回复{"username":"Duplicate Username"}
或者我想在引发ValidationError时指定状态代码:
def validate_username(self, value):
if User.objects.filter(username=value).exists():
raise serializers.ValidationError('Duplicate Username',
status_code=status.HTTP_409_CONFLICT)
return value
但这不起作用,因为ValidationError
只返回400。
还有其他方法可以实现这个目标吗?
答案 0 :(得分:21)
您可以提出不同的例外情况,例如:
from rest_framework.exceptions import APIException
from django.utils.encoding import force_text
from rest_framework import status
class CustomValidation(APIException):
status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
default_detail = 'A server error occurred.'
def __init__(self, detail, field, status_code):
if status_code is not None:self.status_code = status_code
if detail is not None:
self.detail = {field: force_text(detail)}
else: self.detail = {'detail': force_text(self.default_detail)}
您可以在序列化程序中使用它,例如:
raise CustomValidation('Duplicate Username','username', status_code=status.HTTP_409_CONFLICT)
或
raise CustomValidation('Access denied','username', status_code=status.HTTP_403_FORBIDDEN)
答案 1 :(得分:3)
使用django-rest-framework自定义异常处理程序http://www.django-rest-framework.org/api-guide/exceptions/
def custom_exception_handler(exc, context=None):
response = exception_handler(exc, context)
if response is not None:
if response.data['detail'] == 'Duplicate Username':
response.data['username'] = response.data.pop('detail')
response.status_code = status.HTTP_409_CONFLICT
return response
答案 2 :(得分:1)
默认情况下,加注serializers.ValidationError
将返回HTTP_400_BAD_REQUEST
但是有时候我们想返回带有正常ValidationError
状态代码的200
,因为客户端的some库无法在响应代码时解析json
响应数据不是200
。
我尝试了这个。但这不起作用:
raise serializers.ValidationError({'message':'Invalid email address'}, code=200)
所以我们可以做到这一点,并且有效:
res = serializers.ValidationError({'message':'Invalid email address'})
res.status_code = 200
raise res
答案 3 :(得分:0)
为了补充 Anush Devendra 的回答,似乎提出除 ValidationError
以外的任何内容都将绕过 DRF 在其他领域所做的处理。
考虑来自 exceptions.py 中 DRF 的这段代码:
def to_internal_value(self, data):
[...]
for field in fields:
[...]
try:
validated_value = field.run_validation(primitive_value)
if validate_method is not None:
validated_value = validate_method(validated_value)
except ValidationError as exc:
errors[field.field_name] = exc.detail
[...]
else:
set_value(ret, field.source_attrs, validated_value)
if errors:
raise ValidationError(errors)
return ret
如果你想得到这样的答案:
{
"my_first_field": [
"The first field had an error."
],
"my_second_field": [
"The second field had an error."
],
}
您需要在 validate_ValidationError
。
请注意,这样做您将无法从 ValidationError
继承自定义错误并且 status_code
与 400 不同。您的 detail
消息将被提取并生成一个新的ValidationError
(默认为 400 status_code
)加注。