DRF中is_valid()为何需传raise_exception=True才能返回格式化错误
问题根因
两个场景的表现差异和is_valid()是否传raise_exception=True没有本质关系,核心问题出在密码修改视图的状态码使用错误,以及对raise_exception参数作用的认知偏差:
- 电影接口校验失败时手动返回的是
400 Bad Request状态码,这是HTTP标准里定义的客户端请求参数错误专用状态码,所有HTTP客户端(包括Postman、浏览器)都会正常解析该状态码对应的响应体,所以不需要加raise_exception=True也能正常看到格式化的错误信息。 - 密码修改接口的原始逻辑里,校验失败分支手动返回的是
304 Not Modified状态码,这个状态码是HTTP缓存协商流程专用的,RFC规范明确要求304响应绝对不能携带响应体。所有标准HTTP客户端收到304状态码时,会直接忽略响应返回的内容、读取本地缓存的资源,所以你在Postman里会看到无响应的表现,和序列化器的校验逻辑没有任何关系。
为什么加了raise_exception=True就恢复正常
当给is_valid()传入raise_exception=True参数时,序列化器校验失败不会返回False走到你写的304返回分支,而是直接抛出ValidationError异常。这个异常会被DRF内置的全局异常处理器自动捕获,直接返回400状态码+格式化错误信息的标准响应,相当于绕过了你写的错误的304响应逻辑,所以才能正常展示错误。
额外的代码优化建议
- 不要在序列化器的
validate方法里做数据持久化操作:当前在密码校验通过后直接在validate里调用user.set_password()和user.save()是不符合DRF设计规范的,validate方法只应该做参数合法性校验,数据修改的逻辑应该写到序列化器的update/create方法中,由视图调用serializer.save()触发,避免校验流程中掺杂写操作导致的数据不一致问题。 - 状态码使用要符合规范:304只能用于缓存协商场景,不能用来表示业务处理失败;密码修改是同步完成的操作,处理成功返回200 OK即可,不需要用202 Accepted(202用于异步处理、任务已提交但未完成的场景)。
内容的提问来源于stack exchange,提问作者Partho Debnath
相关产品推荐
相关产品推荐

