@@ -699,8 +699,14 @@ def _generate_operation_id(self) -> str:
699
699
class ResponseBuilder (Generic [ResponseEventT ]):
700
700
"""Internally used Response builder"""
701
701
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
+ ):
703
708
self .response = response
709
+ self .serializer = serializer
704
710
self .route = route
705
711
706
712
def _add_cors (self , event : ResponseEventT , cors : CORSConfig ):
@@ -783,6 +789,11 @@ def build(self, event: ResponseEventT, cors: Optional[CORSConfig] = None) -> Dic
783
789
self .response .base64_encoded = True
784
790
self .response .body = base64 .b64encode (self .response .body ).decode ()
785
791
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
+
786
797
return {
787
798
"statusCode" : self .response .status_code ,
788
799
"body" : self .response .body ,
@@ -1332,14 +1343,6 @@ def __init__(
1332
1343
1333
1344
self .use ([OpenAPIValidationMiddleware ()])
1334
1345
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
-
1343
1346
def get_openapi_schema (
1344
1347
self ,
1345
1348
* ,
@@ -1717,7 +1720,7 @@ def resolve(self, event, context) -> Dict[str, Any]:
1717
1720
event = event .raw_event
1718
1721
1719
1722
if self ._debug :
1720
- print (self ._json_dump (event ))
1723
+ print (self ._serializer (event ))
1721
1724
1722
1725
# Populate router(s) dependencies without keeping a reference to each registered router
1723
1726
BaseRouter .current_event = self ._to_proxy_event (event )
@@ -1881,19 +1884,23 @@ def _not_found(self, method: str) -> ResponseBuilder:
1881
1884
if method == "OPTIONS" :
1882
1885
logger .debug ("Pre-flight request detected. Returning CORS with null response" )
1883
1886
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
+ )
1885
1891
1886
1892
handler = self ._lookup_exception_handler (NotFoundError )
1887
1893
if handler :
1888
- return self ._response_builder_class (handler (NotFoundError ()))
1894
+ return self ._response_builder_class (response = handler (NotFoundError ()), serializer = self . _serializer )
1889
1895
1890
1896
return self ._response_builder_class (
1891
- Response (
1897
+ response = Response (
1892
1898
status_code = HTTPStatus .NOT_FOUND .value ,
1893
1899
content_type = content_types .APPLICATION_JSON ,
1894
1900
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" },
1896
1902
),
1903
+ serializer = self ._serializer ,
1897
1904
)
1898
1905
1899
1906
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
1903
1910
self ._reset_processed_stack ()
1904
1911
1905
1912
return self ._response_builder_class (
1906
- self ._to_response (
1913
+ response = self ._to_response (
1907
1914
route (router_middlewares = self ._router_middlewares , app = self , route_arguments = route_arguments ),
1908
1915
),
1909
- route ,
1916
+ serializer = self ._serializer ,
1917
+ route = route ,
1910
1918
)
1911
1919
except Exception as exc :
1912
1920
# 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
1920
1928
# we'll let the original exception propagate, so
1921
1929
# they get more information about what went wrong.
1922
1930
return self ._response_builder_class (
1923
- Response (
1931
+ response = Response (
1924
1932
status_code = 500 ,
1925
1933
content_type = content_types .TEXT_PLAIN ,
1926
1934
body = "" .join (traceback .format_exc ()),
1927
1935
),
1928
- route ,
1936
+ serializer = self ._serializer ,
1937
+ route = route ,
1929
1938
)
1930
1939
1931
1940
raise
@@ -1958,18 +1967,19 @@ def _call_exception_handler(self, exp: Exception, route: Route) -> Optional[Resp
1958
1967
handler = self ._lookup_exception_handler (type (exp ))
1959
1968
if handler :
1960
1969
try :
1961
- return self ._response_builder_class (handler (exp ), route )
1970
+ return self ._response_builder_class (response = handler (exp ), serializer = self . _serializer , route = route )
1962
1971
except ServiceError as service_error :
1963
1972
exp = service_error
1964
1973
1965
1974
if isinstance (exp , ServiceError ):
1966
1975
return self ._response_builder_class (
1967
- Response (
1976
+ response = Response (
1968
1977
status_code = exp .status_code ,
1969
1978
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 },
1971
1980
),
1972
- route ,
1981
+ serializer = self ._serializer ,
1982
+ route = route ,
1973
1983
)
1974
1984
1975
1985
return None
@@ -1995,12 +2005,9 @@ def _to_response(self, result: Union[Dict, Tuple, Response]) -> Response:
1995
2005
return Response (
1996
2006
status_code = status_code ,
1997
2007
content_type = content_types .APPLICATION_JSON ,
1998
- body = self . _json_dump ( result ) ,
2008
+ body = result ,
1999
2009
)
2000
2010
2001
- def _json_dump (self , obj : Any ) -> str :
2002
- return self ._serializer (obj )
2003
-
2004
2011
def include_router (self , router : "Router" , prefix : Optional [str ] = None ) -> None :
2005
2012
"""Adds all routes and context defined in a router
2006
2013
0 commit comments