FastAPI handling camelCase and PascalCase same time

Question:

How can I handling PascalCase and camelCase requests bodies to snake_case same time in FastAPI app?
I tried to use middleware and routerHandler to replace camelCase to PascalCase, but it works not so good.

class CustomRouteHandler(APIRoute):

    def get_route_handler(self) -> Callable:
        original_route_handler = super().get_route_handler()

        async def custom_route_handler(request: Request) -> Response:
            route_path = request.url.path
            body = await request.body()
            logger.info({"path": request.url.path, "request": request._body.decode("utf-8")})
            if body:
                body = ujson.dumps(humps.pascalize(ujson.loads(body.decode("utf-8")))).encode("ascii")
            request._body = body
            try:
                return await original_route_handler(request)
            except ValidationError as e:
                logger.exception(e, exc_info=True)
                return UJSONResponse(status_code=200, content={"Success": False, "Message": e})
            
        return custom_route_handler

router = APIRouter(prefix="/payments", route_class=CustomRouteHandler)

When I logging this code, all fine. But it returns ValidationError:

request body: {"test": 12345}
logger after pascalize: {"Test": 12345}
ERROR: 1 validation error for Requestnbody -> Test 
none is not an allowed value (type=type_error.none.not_allowed)
Asked By: karvetskiy

||

Answers:

First of all you should understand that when we are using CamelCase in API requests, the request body will be converted into a string by default. So, if you want to handle both camelCase and PascalCase, you need to handle both cases. The easiest way is to add two routes – one for each case. For example:

class CustomRouteHandler(APIRoute): 

def get_route_handler(self) -> Callable: original_route_handler = super().get_route_handler() 

async def custom_route_handler(request: Request) -> Response: route_path = request.url.path body = await request.body()

logger.info("Path": request.url.path, "Request": request._body.decode("utf-8"))

if body: body = ujson.dumps(humps.pascalize(ujson.loads(body.decode("utf-8")))).encode("ascii") 
request._body = body

try: return await original_route_handler(request) 
except ValidationError as e: logger.exception(e, exc_info=True) 
return UJSONResponse(status_code=200, content="Success", "Message": e)

return custom_route_handler router = APIRouter(prefix="/payments", route_class=CustomRouteHandler)

Then you can create a middleware which handles both cases:

middleware = RouterMiddleware(prefix="/payments", route_class="custom_route_handler")

And then you can use your custom routing handler in your main route:

main_routes = Routes.new(Router())

You can see more examples on the official docs page.

Answered By: Lonely Dreamer
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.