Reputation: 478
I'm running an api on apache/2.4.29 built using django rest framework 3.10.3 on top of django 2.2.5 running on wsgi, whenever I try to return a json object for a POST request from a client, the POST method inside apiviews.py that describes success does not send the full data. I have tried on using curl, postman and insomina and the response is the same. On the log it shows that the returning Reponse object is fully sent. This is the apiviews.py code:
from rest_framework import generics, status, viewsets, permissions
from rest_framework.response import Response
from rest_framework.views import APIView
from django.shortcuts import get_object_or_404, get_list_or_404
from django.contrib.auth import authenticate
import threading
import datetime
import json
from commons.models import SMSMessages
from commons.serializers import SMSMessagesSerializer
from notification.sender import sender
class SMSView(APIView):
def post(self, request):
sms_messages_serializer = SMSMessagesSerializer(
data={
"sms_number_to": request.data.get("sms_number_to"),
"sms_content": request.data.get("sms_content"),
"sending_user": request.auth.user_id,
}
)
permission_classes = (permissions.IsAuthenticated)
if sms_messages_serializer.is_valid():
data_to_send = {
"number": sms_messages_serializer.validated_data[
"sms_number_to"
],
"msg_text": sms_messages_serializer.validated_data[
"sms_content"
]
}
sms_object = sms_messages_serializer.save()
else:
print("invalid data - {0}\t{1}".format(sms_messages_serializer.errors, datetime.datetime.now()))
data_to_send = None
max_retry = 0
resp = Response()
while max_retry < 3:
max_retry += 1
status_flag, status_response = sender(data_to_send)
if not status_flag:
resp = Response(
data={
"status": "sms not sent"
},
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
content_type="application/json"
)
else:
# the update method defined in the SMSMessagesSerializer class
# needs an instance to work with.
sms_messages_serializer.update(
sms_object,
{
"delivery_status": True
}
)
resp = Response(
data={
"status": "sms successfully sent"
},
headers=status_response.headers,
status=status_response.status_code,
content_type="application/x-www-form-urlencoded"
)
print(resp.data)
return resp
else:
resp = Response(
data={
"error": "unable to send sms"
},
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
content_type="application/json"
)
print(resp.data)
return resp
when sms is successfully sent, on the log file it prints out:
[Wed Dec 23 14:19:29.789772 2020] [wsgi:error] [pid 27330] [remote xxx.xxx.xxx.xxx:xxxx] {'status': 'sms successfully sent.'}
but this is not what is delivered to the client, the client application receives:
{"status":"sms successfull
This is the sender
module for more clarity -- it uses the python request library:
import requests
import time
import json
from rest_framework.response import Response
from rest_framework import status
base_url = "http://xxx.xxx.xxx.xxx:xxxx/"
def sender(sms_data):
"""
The actual function that accesses the server and sends the sms.
"""
sending_url = base_url + "api/sendsms/"
sending_headers = {"content-type": "application/x-www-form-urlencoded"}
response = requests.Response()
try:
response = requests.post(
sending_url,
data=sms_data,
headers=sending_headers,
timeout=(7, 14),
)
response.raise_for_status()
except Exception as e:
print("{}".format(e))
return False, response
else:
return True, response
with all the headers intact and this only occurs on the success response, it does not appear on the rest.
How do I make it send the full response to the client?
thank you
Upvotes: 1
Views: 332
Reputation: 38922
The headers set on successful response inform the wrong Content-Length and Content-Type.
Your response is currently built like so:
resp = Response(
data={
"status": "success"
},
headers=status_response.headers,
status=status_response.status_code,
content_type="application/form-data"
)
There you're forwarding headers of the response from the SMS service such as Content-Length
, whereas the transferred content is {"status": "success"}
.
When Content-Length
is not provided in the built response, the framework computes it based on the transferred content.
One more thing to note is that the transferred content is JSON data and not form-data
.
I recommend explicitly selecting which headers to send back to the requestor if you care about any specific headers in the SMS service response like so:
resp = Response(
data={
"status": "success"
},
headers={
"specific_header_name_passed_from_sms_service": status_response.headers["specific_header_name_passed_from_sms_service"],
#...
},
status=status_response.status_code,
content_type="application/json"
)
Otherwise if you don't care for any of the headers in the response from the SMS service, you can ignore passing headers
option in the built response.
Upvotes: 1