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

18 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-05-21 20:31 +0100

1from nexios.http import Request, Response 

2import typing 

3from typing_extensions import Annotated, Doc, Any 

4 

5 

6class 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