0

What is the correct way to just route a front-end request (that used to work) through a gateway to a service (that used to work). (Even now, if I send the front-end request strait to the service everything still works, so it it just my gateway view that is not handling and forwarding the request properly)

My front-end used to post images with user posts to my back-end just fine. But I have implemented a gateway and now the only thing I am still struggling with is just forwarding a file that the user posts to the required service:

All my gateways just json.dumps() the incoming request data and moves on, but with the image I get an error along the lines of: TypeError: Object of type InMemoryUploadedFile is not JSON serializable - which I perfectly understand, but what is the correct way to send the image along to the service?

My gateway view:

class DailyReportImages(APIView):

    def post(self, request, *args, **kwargs):
        url = INSERT_DAILY_REPORT_IMAGE
        res = requests.post(url, headers=request.headers, data=json.dumps(request.data), files=request.files)
        return Response(res.json(), res.status_code)

This is what I have for now after days of searching, and at least it reaches the backend now, but it still gives almost every error in the book depending on how I tweak it...

res = requests.post(url, headers=request.headers, data={"name": "test", "report": "myreport"}, files={'Screenshot from 2020-01-06 19-04-33.png': request.FILES["image"]})

For instance service complains that the name/report values are empty...

Alfa Bravo
  • 1,961
  • 2
  • 25
  • 45

1 Answers1

1

TL;DR

a minimal verifiable example is here,

import requests
from rest_framework.response import Response
from rest_framework.views import APIView


class DailyReportImages(APIView):

    def post(self, request, *args, **kwargs):
        url = 'http://httpbin.org/post'

        cleaned_headers = {
            header_name: header_value for header_name, header_value in request.META.items() if
            type(header_value) == str
        }
        if cleaned_headers.get('CONTENT_TYPE') == 'application/json':
            res = requests.post(url, json=request.data, headers=cleaned_headers)
        else:
            # we must send as `multipart/form-data`
            data_if_any = request.POST.dict()
            files_data_if_any = request.FILES.dict()
            composed_data = {**{key: (None, value) for key, value in data_if_any.items()}, **files_data_if_any}
            res = requests.post(url, files=composed_data, headers=cleaned_headers)

        return Response(res.json(), res.status_code)

Why the current code does not work?

The request.data -- (DRF doc) may contains the uploaded files which is a InMemoryUploadedFile type and is not JSON Serializable.

Note: The OP's code snippet will not raise a "TypeError: Object of type InMemoryUploadedFile is not JSON serializable" error if there is no file object in the payload/request-data

References

  1. You can use the json parameter of requests library to send dict like object
  2. How to send multipart/form-data using requests module?
  3. How to convert QueryDict (or MultiValueDict) to a dict object?
  4. How can I get all the request headers in Django?
JPG
  • 82,442
  • 19
  • 127
  • 206
  • 1
    Thank you! Excellent answer and 100 pts well spend - code works nicely now :) – Alfa Bravo Sep 16 '20 at 09:13
  • Glad that worked!! Please go through the references too, that might useful for you. Also, please do *upvote* the answer if you feel the answer is useful :) – JPG Sep 16 '20 at 10:32