NegassaB
NegassaB

Reputation: 478

django rest framework running on apache2 not returning full response to client

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

Answers (1)

Oluwafemi Sule
Oluwafemi Sule

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

Related Questions