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)
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.
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)
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.