Search code examples
pythonfastapiuvicorn

Accept gzipped body in FastAPI / Uvicorn


I'm using FastAPI with Uvicorn to implement a u-service which accepts a json payload in the request's body. Since the request body can be quite large, I wish the service to accept gzipped. How do I accomplish that?

So far the following:

  • added the GZipMiddleware, but it encodes responses, rather that decoding requests
  • added a 'Content-Encoding: gzip' to my request

Fail with response:

Status: 400 Bad Request
{ "detail": "There was an error parsing the body" }


Solution

  • FastAPI documentation contains an example of a custom gzip encoding request class.

    Note: This page also contains the following phrase: "...if you need Gzip support, you can use the provided GzipMiddleware.", but this is incorrect, since you correctly noticed that middleware only works for responses.

    import gzip
    from typing import Callable, List
    
    from fastapi import Body, FastAPI, Request, Response
    from fastapi.routing import APIRoute
    
    
    class GzipRequest(Request):
        async def body(self) -> bytes:
            if not hasattr(self, "_body"):
                body = await super().body()
                if "gzip" in self.headers.getlist("Content-Encoding"):
                    body = gzip.decompress(body)
                self._body = body
            return self._body
    
    
    class GzipRoute(APIRoute):
        def get_route_handler(self) -> Callable:
            original_route_handler = super().get_route_handler()
    
            async def custom_route_handler(request: Request) -> Response:
                request = GzipRequest(request.scope, request.receive)
                return await original_route_handler(request)
    
            return custom_route_handler
    
    
    app = FastAPI()
    app.router.route_class = GzipRoute
    
    
    @app.post("/sum")
    async def sum_numbers(numbers: List[int] = Body(...)):
        return {"sum": sum(numbers)}