Django REST自定义字段校验函数无法返回400响应的修复方案
解决Django REST中自定义校验函数无法返回400响应的问题
你遇到的问题很典型——你的check_field函数确实生成了错误响应,但你只是调用了它,却没有把函数返回的响应作为视图的返回值返回,所以视图会继续执行后面的代码,不会中断请求流程。
问题根源
当你调用check_field(phone)时,函数内部判断字段为空后会返回一个JsonResponse对象,但这个返回值并没有被你的pay视图接收并返回给客户端。视图会忽略这个返回值,继续执行后续的校验逻辑,自然达不到提前返回错误的效果。
修复方案一:完善函数调用逻辑
首先,我们需要修改check_field函数,让它能接收字段名称(不然字段为空时会显示None field is empty,这不太友好),然后在视图中接收函数的返回值,若存在则直接返回:
- 修改
check_field函数:
def check_field(field_value, field_name): if not field_value: logger.info('its work') return JsonResponse({ 'status': 'failed', 'errors': { 'code': 400, 'message': '{} field is empty'.format(field_name) }, })
- 在视图中调用并处理返回值:
def pay(request): body = request.body.decode('utf-8') if not body: return JsonResponse({ 'status': 'failed', 'errors': { 'code': 400, 'message': 'empty query' }, }) body = json.loads(body) phone = body.get('phone') amount = body.get('amount') merch_name = body.get('merchant_name') # 逐个检查字段,若函数返回响应则直接返回给客户端 error_response = check_field(phone, 'phone') if error_response: return error_response error_response = check_field(amount, 'amount') if error_response: return error_response error_response = check_field(merch_name, 'merchant_name') if error_response: return error_response # 后续支付逻辑继续执行...
修复方案二:用异常处理简化代码
如果觉得逐个判断返回值太繁琐,可以用自定义异常来实现更简洁的逻辑:
- 定义自定义异常类:
class RequiredFieldEmpty(Exception): def __init__(self, field_name): self.field_name = field_name
- 修改
check_field函数抛出异常:
def check_field(field_value, field_name): if not field_value: logger.info('its work') raise RequiredFieldEmpty(field_name)
- 在视图中捕获异常并返回响应:
def pay(request): body = request.body.decode('utf-8') if not body: return JsonResponse({ 'status': 'failed', 'errors': { 'code': 400, 'message': 'empty query' }, }) body = json.loads(body) phone = body.get('phone') amount = body.get('amount') merch_name = body.get('merchant_name') try: check_field(phone, 'phone') check_field(amount, 'amount') check_field(merch_name, 'merchant_name') except RequiredFieldEmpty as e: return JsonResponse({ 'status': 'failed', 'errors': { 'code': 400, 'message': '{} field is empty'.format(e.field_name) }, }) # 后续支付逻辑继续执行...
更规范的优化建议:使用DRF序列化器
既然你在用Django REST Framework,其实最规范的做法是用序列化器来处理字段校验,它不仅能帮你处理必填项检查,还能支持字段类型、格式等更多校验场景,代码也更简洁:
from rest_framework import serializers from rest_framework.response import Response from rest_framework.decorators import api_view # 定义序列化器,指定必填字段和自定义错误信息 class PaymentSerializer(serializers.Serializer): phone = serializers.CharField(required=True, error_messages={'required': 'phone field is empty'}) amount = serializers.CharField(required=True, error_messages={'required': 'amount field is empty'}) merchant_name = serializers.CharField(required=True, error_messages={'required': 'merchant_name field is empty'}) @api_view(['POST']) def pay(request): # 用请求数据初始化序列化器 serializer = PaymentSerializer(data=request.data) # 执行校验,不通过则返回错误响应 if not serializer.is_valid(): # 提取第一个错误信息返回(也可以返回所有错误) first_error = next(iter(serializer.errors.values()))[0] return Response({ 'status': 'failed', 'errors': { 'code': 400, 'message': first_error } }, status=400) # 校验通过后,直接获取清理后的合法数据 validated_data = serializer.validated_data phone = validated_data['phone'] amount = validated_data['amount'] merch_name = validated_data['merchant_name'] # 后续支付逻辑继续执行...
这种方式完全避免了重复的校验代码,而且符合DRF的最佳实践,维护起来更方便。
内容的提问来源于stack exchange,提问作者user12297114
相关产品推荐
相关产品推荐

