@@ -699,8 +699,14 @@ def _generate_operation_id(self) -> str:
699699class ResponseBuilder (Generic [ResponseEventT ]):
700700 """Internally used Response builder"""
701701
702- def __init__ (self , response : Response , route : Optional [Route ] = None ):
702+ def __init__ (
703+ self ,
704+ response : Response ,
705+ serializer : Callable [[Any ], str ] = json .dumps ,
706+ route : Optional [Route ] = None ,
707+ ):
703708 self .response = response
709+ self .serializer = serializer
704710 self .route = route
705711
706712 def _add_cors (self , event : ResponseEventT , cors : CORSConfig ):
@@ -783,6 +789,11 @@ def build(self, event: ResponseEventT, cors: Optional[CORSConfig] = None) -> Dic
783789 self .response .base64_encoded = True
784790 self .response .body = base64 .b64encode (self .response .body ).decode ()
785791
792+ # We only apply the serializer when the content type is JSON and the
793+ # body is not a str, to avoid double encoding
794+ elif self .response .is_json () and not isinstance (self .response .body , str ):
795+ self .response .body = self .serializer (self .response .body )
796+
786797 return {
787798 "statusCode" : self .response .status_code ,
788799 "body" : self .response .body ,
@@ -1332,14 +1343,6 @@ def __init__(
13321343
13331344 self .use ([OpenAPIValidationMiddleware ()])
13341345
1335- # When using validation, we need to skip the serializer, as the middleware is doing it automatically.
1336- # However, if the user is using a custom serializer, we need to abort.
1337- if serializer :
1338- raise ValueError ("Cannot use a custom serializer when using validation" )
1339-
1340- # Install a dummy serializer
1341- self ._serializer = lambda args : args # type: ignore
1342-
13431346 def get_openapi_schema (
13441347 self ,
13451348 * ,
@@ -1717,7 +1720,7 @@ def resolve(self, event, context) -> Dict[str, Any]:
17171720 event = event .raw_event
17181721
17191722 if self ._debug :
1720- print (self ._json_dump (event ))
1723+ print (self ._serializer (event ))
17211724
17221725 # Populate router(s) dependencies without keeping a reference to each registered router
17231726 BaseRouter .current_event = self ._to_proxy_event (event )
@@ -1881,19 +1884,23 @@ def _not_found(self, method: str) -> ResponseBuilder:
18811884 if method == "OPTIONS" :
18821885 logger .debug ("Pre-flight request detected. Returning CORS with null response" )
18831886 headers ["Access-Control-Allow-Methods" ] = "," .join (sorted (self ._cors_methods ))
1884- return ResponseBuilder (Response (status_code = 204 , content_type = None , headers = headers , body = "" ))
1887+ return ResponseBuilder (
1888+ response = Response (status_code = 204 , content_type = None , headers = headers , body = "" ),
1889+ serializer = self ._serializer ,
1890+ )
18851891
18861892 handler = self ._lookup_exception_handler (NotFoundError )
18871893 if handler :
1888- return self ._response_builder_class (handler (NotFoundError ()))
1894+ return self ._response_builder_class (response = handler (NotFoundError ()), serializer = self . _serializer )
18891895
18901896 return self ._response_builder_class (
1891- Response (
1897+ response = Response (
18921898 status_code = HTTPStatus .NOT_FOUND .value ,
18931899 content_type = content_types .APPLICATION_JSON ,
18941900 headers = headers ,
1895- body = self . _json_dump ( {"statusCode" : HTTPStatus .NOT_FOUND .value , "message" : "Not found" }) ,
1901+ body = {"statusCode" : HTTPStatus .NOT_FOUND .value , "message" : "Not found" },
18961902 ),
1903+ serializer = self ._serializer ,
18971904 )
18981905
18991906 def _call_route (self , route : Route , route_arguments : Dict [str , str ]) -> ResponseBuilder :
@@ -1903,10 +1910,11 @@ def _call_route(self, route: Route, route_arguments: Dict[str, str]) -> Response
19031910 self ._reset_processed_stack ()
19041911
19051912 return self ._response_builder_class (
1906- self ._to_response (
1913+ response = self ._to_response (
19071914 route (router_middlewares = self ._router_middlewares , app = self , route_arguments = route_arguments ),
19081915 ),
1909- route ,
1916+ serializer = self ._serializer ,
1917+ route = route ,
19101918 )
19111919 except Exception as exc :
19121920 # If exception is handled then return the response builder to reduce noise
@@ -1920,12 +1928,13 @@ def _call_route(self, route: Route, route_arguments: Dict[str, str]) -> Response
19201928 # we'll let the original exception propagate, so
19211929 # they get more information about what went wrong.
19221930 return self ._response_builder_class (
1923- Response (
1931+ response = Response (
19241932 status_code = 500 ,
19251933 content_type = content_types .TEXT_PLAIN ,
19261934 body = "" .join (traceback .format_exc ()),
19271935 ),
1928- route ,
1936+ serializer = self ._serializer ,
1937+ route = route ,
19291938 )
19301939
19311940 raise
@@ -1958,18 +1967,19 @@ def _call_exception_handler(self, exp: Exception, route: Route) -> Optional[Resp
19581967 handler = self ._lookup_exception_handler (type (exp ))
19591968 if handler :
19601969 try :
1961- return self ._response_builder_class (handler (exp ), route )
1970+ return self ._response_builder_class (response = handler (exp ), serializer = self . _serializer , route = route )
19621971 except ServiceError as service_error :
19631972 exp = service_error
19641973
19651974 if isinstance (exp , ServiceError ):
19661975 return self ._response_builder_class (
1967- Response (
1976+ response = Response (
19681977 status_code = exp .status_code ,
19691978 content_type = content_types .APPLICATION_JSON ,
1970- body = self . _json_dump ( {"statusCode" : exp .status_code , "message" : exp .msg }) ,
1979+ body = {"statusCode" : exp .status_code , "message" : exp .msg },
19711980 ),
1972- route ,
1981+ serializer = self ._serializer ,
1982+ route = route ,
19731983 )
19741984
19751985 return None
@@ -1995,12 +2005,9 @@ def _to_response(self, result: Union[Dict, Tuple, Response]) -> Response:
19952005 return Response (
19962006 status_code = status_code ,
19972007 content_type = content_types .APPLICATION_JSON ,
1998- body = self . _json_dump ( result ) ,
2008+ body = result ,
19992009 )
20002010
2001- def _json_dump (self , obj : Any ) -> str :
2002- return self ._serializer (obj )
2003-
20042011 def include_router (self , router : "Router" , prefix : Optional [str ] = None ) -> None :
20052012 """Adds all routes and context defined in a router
20062013
0 commit comments