Coverage for nexios\middlewares\base.py: 94%

1 from nexios.http import Request, Response

2 import typing

3 from typing_extensions import Annotated, Doc, Any

4

5

6 class BaseMiddleware:

7 """

8 Base middleware class for handling request-response processing in Nexios.

9

10 This class provides a structure for middleware in the Nexios framework.

11 It allows child classes to intercept and modify HTTP requests before they

12 reach the main application logic and modify responses before they are returned

13 to the client.

14

15 Middleware classes inheriting from `BaseMiddleware` should implement:

16

17 - `process_request()`: To inspect, modify, or reject an incoming request.

18 - `process_response()`: To inspect or modify an outgoing response.

19

20 The user can decide when to call `next()` to proceed to the next middleware or handler.

21 """

22

23 def __init__(

24 self,

25 **kwargs: Annotated[

26 typing.Dict[typing.Any, typing.Any],

27 Doc("Additional keyword arguments for middleware configuration."),

28 ],

29 ) -> None:

30 """

31 Initializes the middleware with optional keyword arguments.

32

33 Middleware can accept configuration parameters via `kwargs`. These parameters

34 can be used to modify behavior when subclassing this base class.

35

36 Args:

37 **kwargs (dict): Arbitrary keyword arguments for middleware settings.

38 """

39 pass

40

41 async def __call__(

42 self,

43 request: Annotated[

44 Request,

45 Doc("The incoming HTTP request object representing the client request."),

46 ],

47 response: Annotated[

48 Response,

49 Doc("The HTTP response object that will be returned to the client."),

50 ],

51 call_next: Annotated[

52 typing.Callable[..., typing.Awaitable[Any]],

53 Doc("The next middleware function in the processing chain."),

54 ],

55 ) -> Any:

56 """

57 Handles the request-response cycle for the middleware.

58

59 This method does the following:

60 1. Calls `process_request()` to inspect or modify the request before passing it forward.

61 2. Allows the user to decide when to call `next_middleware()`.

62 3. Calls `process_response()` to modify the response after `next_middleware()` is called.

63

64 Args:

65 request (Request): The incoming HTTP request object.

66 next_middleware (Callable[..., Awaitable[Response]]): A function representing the next middleware.

67

68 Returns:

69 Response: The final HTTP response object.

70 """

71 self._call_next = False

72

73 async def wrapped_call_next() -> Any:

74 self._call_next = True

75 return await call_next() # type:ignore

76

77 await self.process_request(request, response, wrapped_call_next)

78 if self._call_next:

79 await self.process_response(request, response)

80

81 async def process_request(

82 self,

83 request: Annotated[

84 Request, Doc("The HTTP request object that needs to be processed.")

85 ],

86 response: Annotated[

87 Response,

88 Doc("The HTTP response object that will be returned to the client."),

89 ],

90 call_next: Annotated[

91 typing.Callable[..., typing.Awaitable[Response]],

92 Doc("The next middleware or handler to call."),

93 ],

94 ) -> Annotated[

95 Any,

96 Doc("The HTTP response object returned by the next middleware or handler."),

97 ]:

98 """

99 Hook for processing an HTTP request before passing it forward.

100

101 Override this method in subclasses to inspect, modify, or reject requests before

102 they reach the next middleware or the application logic. The user can decide when

103 to call `next()` to proceed.

104

105 Args:

106 request (Request): The incoming HTTP request object.

107 next (Callable[..., Awaitable[Response]]): The next middleware or handler to call.

108

109 Returns:

110 Response: The HTTP response object returned by the next middleware or handler.

111 """

112 return await call_next()

113

114 async def process_response(

115 self,

116 request: Annotated[

117 Request,

118 Doc(

119 "The original HTTP request object, available for context during response processing."

120 ),

121 ],

122 response: Annotated[

123 Response,

124 Doc(

125 "The HTTP response object that can be modified before sending to the client."

126 ),

127 ],

128 ) -> Annotated[

129 Any,

130 Doc("The modified HTTP response object to be returned to the client."),

131 ]:

132 """

133 Hook for processing an HTTP response before returning it to the client.

134

135 Override this method in subclasses to modify response headers, content, or status codes

136 before they are sent back to the client.

137

138 Args:

139 request (Request): The original HTTP request object.

140 response (Response): The outgoing HTTP response object.

141

142 Returns:

143 Response: The modified HTTP response object.

144 """

145 # Default behavior: Return the response as is

146 return response