diff --git a/ninja/operation.py b/ninja/operation.py index 0e4976d6e..3df92b0a5 100644 --- a/ninja/operation.py +++ b/ninja/operation.py @@ -31,6 +31,8 @@ from ninja.throttling import BaseThrottle from ninja.types import DictStrAny from ninja.utils import check_csrf, is_async_callable +from ninja.parser import Parser + if TYPE_CHECKING: from ninja import NinjaAPI, Router # pragma: no cover @@ -60,6 +62,7 @@ def __init__( include_in_schema: bool = True, url_name: Optional[str] = None, openapi_extra: Optional[Dict[str, Any]] = None, + parser: Optional[Parser] = None, ) -> None: self.is_async = False self.path: str = path @@ -72,7 +75,9 @@ def __init__( self.auth_param: Optional[Union[Sequence[Callable], Callable, object]] = auth self.auth_callbacks: Sequence[Callable] = [] self._set_auth(auth) - + + self.parser = parser or Parser() + if isinstance(throttle, BaseThrottle): throttle = [throttle] self.throttle_param = throttle @@ -116,6 +121,12 @@ def __init__( callback(self) def run(self, request: HttpRequest, **kw: Any) -> HttpResponseBase: + if request.body: + try: + self.parser.parse_body(request) + except Exception as e: + return self.api.on_exception(request, e) + error = self._run_checks(request) if error: return error @@ -411,6 +422,7 @@ def add_operation( url_name: Optional[str] = None, include_in_schema: bool = True, openapi_extra: Optional[Dict[str, Any]] = None, + parser: Optional[Parser] = None, ) -> Operation: if url_name: self.url_name = url_name @@ -439,6 +451,7 @@ def add_operation( include_in_schema=include_in_schema, url_name=url_name, openapi_extra=openapi_extra, + parser=parser, ) self.operations.append(operation) diff --git a/ninja/router.py b/ninja/router.py index 39da9020c..9dbd9f147 100644 --- a/ninja/router.py +++ b/ninja/router.py @@ -18,6 +18,7 @@ from ninja.errors import ConfigError from ninja.operation import PathView from ninja.throttling import BaseThrottle +from ninja.parser import Parser from ninja.types import TCallable from ninja.utils import normalize_path, replace_path_param_notation @@ -71,6 +72,7 @@ def get( url_name: Optional[str] = None, include_in_schema: bool = True, openapi_extra: Optional[Dict[str, Any]] = None, + parser: Optional[Parser] = None, ) -> Callable[[TCallable], TCallable]: return self.api_operation( ["GET"], @@ -90,6 +92,7 @@ def get( url_name=url_name, include_in_schema=include_in_schema, openapi_extra=openapi_extra, + parser=parser, ) def post( @@ -111,6 +114,7 @@ def post( url_name: Optional[str] = None, include_in_schema: bool = True, openapi_extra: Optional[Dict[str, Any]] = None, + parser: Optional[Parser] = None, ) -> Callable[[TCallable], TCallable]: return self.api_operation( ["POST"], @@ -130,6 +134,7 @@ def post( url_name=url_name, include_in_schema=include_in_schema, openapi_extra=openapi_extra, + parser=parser, ) def delete( @@ -151,6 +156,7 @@ def delete( url_name: Optional[str] = None, include_in_schema: bool = True, openapi_extra: Optional[Dict[str, Any]] = None, + parser: Optional[Parser] = None, ) -> Callable[[TCallable], TCallable]: return self.api_operation( ["DELETE"], @@ -170,6 +176,7 @@ def delete( url_name=url_name, include_in_schema=include_in_schema, openapi_extra=openapi_extra, + parser=parser, ) def patch( @@ -191,6 +198,7 @@ def patch( url_name: Optional[str] = None, include_in_schema: bool = True, openapi_extra: Optional[Dict[str, Any]] = None, + parser: Optional[Parser] = None, ) -> Callable[[TCallable], TCallable]: return self.api_operation( ["PATCH"], @@ -210,6 +218,7 @@ def patch( url_name=url_name, include_in_schema=include_in_schema, openapi_extra=openapi_extra, + parser=parser, ) def put( @@ -231,6 +240,7 @@ def put( url_name: Optional[str] = None, include_in_schema: bool = True, openapi_extra: Optional[Dict[str, Any]] = None, + parser: Optional[Parser] = None, ) -> Callable[[TCallable], TCallable]: return self.api_operation( ["PUT"], @@ -250,6 +260,7 @@ def put( url_name=url_name, include_in_schema=include_in_schema, openapi_extra=openapi_extra, + parser=parser, ) def api_operation( @@ -272,6 +283,7 @@ def api_operation( url_name: Optional[str] = None, include_in_schema: bool = True, openapi_extra: Optional[Dict[str, Any]] = None, + parser: Optional[Parser] = None, ) -> Callable[[TCallable], TCallable]: def decorator(view_func: TCallable) -> TCallable: self.add_api_operation( @@ -293,6 +305,7 @@ def decorator(view_func: TCallable) -> TCallable: url_name=url_name, include_in_schema=include_in_schema, openapi_extra=openapi_extra, + parser=parser, ) return view_func @@ -319,6 +332,7 @@ def add_api_operation( url_name: Optional[str] = None, include_in_schema: bool = True, openapi_extra: Optional[Dict[str, Any]] = None, + parser: Optional[Parser] = None, ) -> None: if path not in self.path_operations: path_view = PathView() @@ -352,6 +366,7 @@ def add_api_operation( url_name=url_name, include_in_schema=include_in_schema, openapi_extra=openapi_extra, + parser=parser, ) if self.api: path_view.set_api_instance(self.api, self)