Django REST Framework中detail_route的PUT请求无末尾斜杠时请求体为空的原因探究
这个问题其实是Django默认的URL规则和HTTP客户端行为共同作用的结果,我给你一步步拆解清楚为什么会出现这种「无报错但请求体为空」的情况:
1. Django默认的APPEND_SLASH机制
Django项目默认开启了APPEND_SLASH = True(在settings.py中),这个设置的核心逻辑是:当请求的URL末尾没有斜杠,且路由系统找不到匹配的条目时,它会自动检查添加末尾斜杠后的URL是否存在匹配项。如果存在,就返回301 Moved Permanently重定向响应,引导客户端访问带斜杠的正确URL。
在你的场景里,router.register生成的detail_route接口URL是带末尾斜杠的(即/my_resource/history/),当你请求不带斜杠的/my_resource/history时,Django找不到对应的路由,直接触发了这个重定向逻辑。
2. HTTP客户端对301重定向的PUT请求处理
当requests库收到301重定向响应时,它会自动按照响应的Location头发起新的PUT请求到带斜杠的URL,但这里有个关键细节:重定向后的PUT请求不会携带原请求体。
这是因为HTTP标准中,301重定向的语义是「永久移动」,对于PUT这类有副作用的非GET请求,客户端为了避免重复执行可能产生的意外影响(比如重复修改资源),通常不会自动携带原请求体发起第二次请求。requests库的默认行为就是如此,所以第二次PUT请求的body是空的。
3. 为什么没有报错?
因为重定向后的URL是完全有效的(匹配到了你的history detail_route),DRF会正常处理这个请求——只是此时请求体是空的,所以你的print(request.body)输出空内容,但不会返回404或其他错误响应。
快速验证方法
你可以关闭自动重定向,直接查看第一次请求的状态码来确认这个逻辑:
response = requests.put(url, data=json.dumps({'test': 'passed'}), allow_redirects=False) print(response.status_code) # 会输出301 print(response.headers['Location']) # 会指向带斜杠的目标URL
内容的提问来源于stack exchange,提问作者Egor Wexler

