2

I've Created a middleware to check every request's header if it's header is application urlencoded content type covert the request data to dictionary but it's throwing some errors and not working

Middleware.py

from json import JSONDecodeError
from fastapi import  HTTPException, Request
import urllib.parse

import json
from starlette.middleware.base import BaseHTTPMiddleware

class MyMiddleware(BaseHTTPMiddleware):
     async def dispatch(self, request: Request , call_next):
            response = await call_next(request)
            content_type = response.headers.get('Content-Type')
            if content_type == 'application/json':
                try:
                     response = await request.json() 
                     return response
                except JSONDecodeError:
                    raise HTTPException(status_code=400, detail='Invalid JSON data')

            elif (content_type == 'application/x-www-form-urlencoded' or
                content_type.startswith('multipart/form-data')):
        
                try:
                    payload = await request.body()
                    payload = payload.decode()
                    payload =   urllib.parse.parse_qs(payload)
                    new_dict ={}
                    for key, value in payload.items():
                        for val in value:
                            new_dict[key]=val
                    print(type(new_dict))
                    payload = json.dumps(new_dict)
                    response = new_dict
                    return  response

                except Exception:
                    raise HTTPException(status_code=400, detail='Invalid Form data')
            else:
                raise HTTPException(status_code=400, detail='Invalid  data')

main.py

from fastapi import FastAPI
from middleware.jsonconvertermiddleware import MyMiddleware
from models.index import *
from utils import *
from fastapi.middleware.cors import CORSMiddleware
from routes.user import user_route

app = FastAPI(default_response_class=ApiResponse)
origins = [
    'http://localhost',
    'http://localhost:4200',
]
app.add_middleware(
    CORSMiddleware,
    allow_origins=['*'],
    allow_credentials=True,
    allow_methods=['*'],
    allow_headers=['*']
)
app.add_middleware(MyMiddleware)
app.include_router(user_route)

ERROR

enter image description here

Chris
  • 18,724
  • 6
  • 46
  • 80
  • Please have a look at related answers [here](https://stackoverflow.com/a/71526036/17865804), [here](https://stackoverflow.com/a/71107848/17865804), as well as [here](https://stackoverflow.com/a/71883126/17865804) and [here](https://stackoverflow.com/a/73464007/17865804). You could also use Option 1 of [this answer](https://stackoverflow.com/a/74015930/17865804) (which seems that you already do that) and have it added to the global dependencies, as described [here](https://stackoverflow.com/a/74019824/17865804). – Chris Dec 31 '22 at 11:06

1 Answers1

0

It seems that you are calling the body attribute on the class StreamingResponse.

Well from the starlette source code : this class has no body attribute. So what you should do before calling the body attribute is checking the type of the response you received :

In your middleware here, after the first line :

response = await call_next(request)
# print(reponse.body) <== REMOVE THIS FIRST AND CHECK THE TYPE OF THE RESPONSE

You should check :

if isinstance(response, StreamingResponse):
    async for body in response.body_iterator:
        if not isinstance(body, bytes):
            body = chunk.encode(response.charset)
        # do whatever you wanted to do with the body here ! 

I basically copied this code snipet from the source code itself : here

I did not tested it either, so it might not work as it is, but the idea is here. Use the body_iterator attribute and iterate over it to get the actual "bodies" which are chunk (since it is a stream!)

If the ansnwer needs change for a working solution, please update.

jossefaz
  • 3,312
  • 4
  • 17
  • 40
  • no i've created the middleware in my main.py but still not working – Shahbaz Ali Dec 30 '22 at 12:39
  • what does mean "still not working" ? what error do you get now ? Also please provide an updated version of your code after trying to change it as suggested. You can do so in your question by adding an "UPDATE" section by the end – jossefaz Dec 30 '22 at 12:52
  • I'm updating my shared code in question – Shahbaz Ali Dec 30 '22 at 13:12
  • I want to create a middleware which checks every incoming request header's content type if the content type is application -x urlencoded then convert the request's body into dictionary so how could I achieve it? – Shahbaz Ali Dec 30 '22 at 13:17
  • Base on your edited code : you did not took in consideration the "SteamingResponse type which again does not have a ody attribute so you still continue to get the same error. You first need to check the response type as I wrote in the answer. – jossefaz Dec 30 '22 at 13:34