I've encountered a situation with my Django Rest Framework (DRF) app in which a client mobile app is calling one of my endpoints and is getting a 400 error, but I can't debug what's going wrong. This is what appears in the log:
Sep 02 11:11:29 myapp heroku/router at=info method=POST path="/users/generate_otp/?device_token=cBB2x2Y2L04qpQCbYukR31%3AAPA91bEHqCZ3ztI9wim0EzxVZ1Nv6clZMfsDxw7_6reWIVkm5dQcxuWlifnfxkn7Ope_2wTM75_TMw6BV-pAZyEYdrICz1dsk2gX6_aYJwz0-H3SDR2vLWvceiioUs21dTwXQIMwmBN1&mobile=%2B13104014335&device_type=ios" host=myhost.com request_id=fad10208-c01d-4f64-9640-1aff889ab3ee fwd="66.246.86.216" dyno=web.1 connect=0ms service=1ms status=400 bytes=28 protocol=https
If I send the same URL via Postman the endpoint returns successfully:
Sep 02 11:41:48 myapp heroku/router at=info method=POST path="/users/generate_otp/?device_token=cKY2x2Y2L04qpQCbYukR31%3AAPA91bEHqCZ3ztI9wim0EzxVZ1Nv6clZMfsDxw7_6reWIVkm5dQcxuWlifnfxkn7Ope_2wTM75_TMw6BV-pAZyEYdrICz1dsk2gX6_aYJwz0-H3SDR2vLWvceiioUs21dTwXQIMwmBN1&mobile=%2B13104014335&device_type=ios" host=myhost.com request_id=4ddabe5f-619d-4665-aff5-28afb9ac277d fwd="66.246.86.216" dyno=web.1 connect=0ms service=572ms status=200 bytes=1686 protocol=https
I've tried to examine the request that comes in, by making these the first lines of the endpoint:
@action(detail=False, methods=["POST"])
def generate_otp(self, request):
"""
Text a one-time password to the given mobile number to login.
If there's not yet a user with that number, register them.
"""
print("IN GENERATE OTP", flush=True)
print("IN GENERATE OTP, request.data =", request.data, flush=True)
print("IN GENERATE OTP, request.headers =", request.headers, flush=True)
But those print statements never get called -- so it's as if it's not entering the endpoint at all. To try to debug what's being sent in the request further, I added this middleware:
class RequestLoggingMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
print("--- RequestLoggingMiddleware ---")
print(request.method, request.path)
print(request.GET)
print(request.POST)
print(request.body)
print("--------------------------------", flush=True)
response = self.get_response(request)
return response
But again nothing is being printed.
I've also set DEBUG = True
in settings.py, and have even added:
logger = logging.getLogger("django.request")
logger.setLevel(logging.DEBUG)
All are attempts to try to get more information on how I could be getting a 400 error for a POST request that seems to work everywhere else. To make matters even more complicated, someone else is running the client mobile app and isn't seeing these results.
What might be going on? What circumstances could be giving me a 400 error before the endpoint is even entered? How can I debug and fix this?
As Joachim guessed in the comments above, the headers were the issue. The front-end team reported today that they'd upgraded some libraries for the new release and when doing so, had neglected to change the header to pass data in the form x-www-form-urlencoded
.
Complicating matters, they'd also erroneously reported that it was working for them, when in fact it wasn't.
It's too bad that I was unable to see more precisely on the backend why the requests were failing, but the error was apparent on from their side once they took a look, and they were able to remedy it.