0
# Request and Response Handling
1
2
Core request/response abstractions used across all transport types. Provides unified interfaces for handling different types of requests with content, parameters, and user context.
3
4
## Capabilities
5
6
### Core Request Interface
7
8
Abstract base class providing unified request handling across all transport types.
9
10
```python { .api }
11
class Request:
12
user: Optional[UUID]
13
has_content: bool
14
has_params: bool
15
async def content(self, **kwargs) -> Any: ...
16
async def params(self, **kwargs) -> dict[str, Any]: ...
17
async def _content(self, **kwargs) -> Any: ... # Abstract
18
async def _params(self, **kwargs) -> dict[str, Any]: ... # Abstract
19
def __eq__(self, other: Any) -> bool: ... # Abstract
20
def __repr__(self) -> str: ... # Abstract
21
```
22
23
### Core Response Interface
24
25
Concrete response class for returning data with status codes.
26
27
```python { .api }
28
class Response:
29
def __init__(self, data: Any = None, *, status: int = 200): ...
30
has_content: bool
31
status: int
32
async def content(self, **kwargs) -> Any: ...
33
def __eq__(self, other: Any) -> bool: ...
34
def __repr__(self) -> str: ...
35
def __hash__(self) -> int: ...
36
37
class ResponseException:
38
def __init__(self, *args, status: int = 400): ...
39
status: int
40
```
41
42
**Usage Examples:**
43
44
```python
45
from minos.networks import Response, ResponseException
46
47
# Create successful response
48
response = Response({"user_id": "123", "name": "John"})
49
print(response.status) # 200
50
51
# Create response with custom status
52
created_response = Response({"id": "123"}, status=201)
53
54
# Check if response has content
55
if response.has_content:
56
data = await response.content()
57
58
# Raise response exception
59
if not valid_data:
60
raise ResponseException("Invalid data", status=400)
61
```
62
63
### In-Memory Request Implementation
64
65
Concrete request implementation for testing and in-memory usage.
66
67
```python { .api }
68
class InMemoryRequest(Request):
69
def __init__(self, content: Any = None, params: dict[str, Any] = None, user: Optional[UUID] = None): ...
70
user: Optional[UUID]
71
has_content: bool
72
has_params: bool
73
async def _content(self, **kwargs) -> Any: ...
74
async def _params(self, **kwargs) -> dict[str, Any]: ...
75
def __eq__(self, other: Any) -> bool: ...
76
def __repr__(self) -> str: ...
77
```
78
79
**Usage Examples:**
80
81
```python
82
from minos.networks import InMemoryRequest
83
from uuid import UUID
84
85
# Create request with content
86
request = InMemoryRequest(
87
content={"name": "John", "email": "john@example.com"},
88
params={"user_id": "123"},
89
user=UUID("12345678-1234-5678-9012-123456789012")
90
)
91
92
# Access request data
93
if request.has_content:
94
content = await request.content()
95
print(content["name"]) # "John"
96
97
if request.has_params:
98
params = await request.params()
99
print(params["user_id"]) # "123"
100
101
print(request.user) # UUID object
102
```
103
104
### Wrapped Request Implementation
105
106
Request wrapper that applies transformations to content and parameters.
107
108
```python { .api }
109
from typing import Callable, Union, Awaitable
110
111
ContentAction = Callable[[Any, ...], Union[Any, Awaitable[Any]]]
112
ParamsAction = Callable[[dict[str, Any], ...], Union[dict[str, Any], Awaitable[dict[str, Any]]]]
113
114
class WrappedRequest(Request):
115
def __init__(self, base: Request, content_action: Optional[ContentAction] = None, params_action: Optional[ParamsAction] = None): ...
116
user: Optional[UUID]
117
has_content: bool
118
has_params: bool
119
async def _content(self, **kwargs) -> Any: ...
120
async def _params(self, **kwargs) -> dict[str, Any]: ...
121
def __eq__(self, other: Any) -> bool: ...
122
def __repr__(self) -> str: ...
123
```
124
125
**Usage Examples:**
126
127
```python
128
from minos.networks import WrappedRequest, InMemoryRequest
129
130
# Base request
131
base_request = InMemoryRequest(content={"name": "john"})
132
133
# Transform content to uppercase
134
def uppercase_name(content):
135
if isinstance(content, dict) and "name" in content:
136
content["name"] = content["name"].upper()
137
return content
138
139
# Create wrapped request with transformation
140
wrapped_request = WrappedRequest(
141
base=base_request,
142
content_action=uppercase_name
143
)
144
145
# Content is transformed when accessed
146
content = await wrapped_request.content()
147
print(content["name"]) # "JOHN"
148
149
# Async transformation example
150
async def async_transform(content):
151
# Simulate async processing
152
await asyncio.sleep(0.1)
153
content["processed"] = True
154
return content
155
156
wrapped_async = WrappedRequest(
157
base=base_request,
158
content_action=async_transform
159
)
160
```
161
162
### Context Variables
163
164
Context variables for passing request metadata across async boundaries.
165
166
```python { .api }
167
from contextvars import ContextVar
168
from uuid import UUID
169
170
REQUEST_USER_CONTEXT_VAR: ContextVar[Optional[UUID]]
171
```
172
173
**Usage Examples:**
174
175
```python
176
from minos.networks import REQUEST_USER_CONTEXT_VAR
177
from uuid import UUID
178
179
# Set user context
180
user_id = UUID("12345678-1234-5678-9012-123456789012")
181
REQUEST_USER_CONTEXT_VAR.set(user_id)
182
183
# Access user context in handlers
184
def get_current_user() -> Optional[UUID]:
185
return REQUEST_USER_CONTEXT_VAR.get(None)
186
187
@enroute.broker.command("user.update")
188
async def update_user(request: Request) -> Response:
189
current_user = get_current_user()
190
if current_user:
191
print(f"User {current_user} is making the request")
192
193
# Process update
194
return Response({"updated": True})
195
```
196
197
## Advanced Usage
198
199
### Custom Request Implementation
200
201
```python
202
class DatabaseRequest(Request):
203
def __init__(self, query_id: str, db_connection):
204
self.query_id = query_id
205
self.db_connection = db_connection
206
self._user = None
207
208
@property
209
def user(self) -> Optional[UUID]:
210
return self._user
211
212
@property
213
def has_content(self) -> bool:
214
return True
215
216
@property
217
def has_params(self) -> bool:
218
return True
219
220
async def _content(self, **kwargs) -> Any:
221
# Fetch content from database
222
result = await self.db_connection.fetch_one(
223
"SELECT content FROM requests WHERE id = ?",
224
(self.query_id,)
225
)
226
return result["content"] if result else None
227
228
async def _params(self, **kwargs) -> dict[str, Any]:
229
# Fetch params from database
230
result = await self.db_connection.fetch_one(
231
"SELECT params FROM requests WHERE id = ?",
232
(self.query_id,)
233
)
234
return result["params"] if result else {}
235
236
def __eq__(self, other):
237
return isinstance(other, DatabaseRequest) and other.query_id == self.query_id
238
239
def __repr__(self):
240
return f"DatabaseRequest(query_id={self.query_id})"
241
```
242
243
### Request/Response Middleware Patterns
244
245
```python
246
class LoggingRequest(WrappedRequest):
247
def __init__(self, base: Request):
248
async def log_content(content):
249
print(f"Accessing content: {type(content)}")
250
return content
251
252
async def log_params(params):
253
print(f"Accessing params: {list(params.keys())}")
254
return params
255
256
super().__init__(
257
base=base,
258
content_action=log_content,
259
params_action=log_params
260
)
261
262
class ValidatedRequest(WrappedRequest):
263
def __init__(self, base: Request, schema: dict):
264
self.schema = schema
265
266
async def validate_content(content):
267
# Validate content against schema
268
if not self._validate(content, self.schema):
269
raise ValueError("Content validation failed")
270
return content
271
272
super().__init__(base=base, content_action=validate_content)
273
274
def _validate(self, data, schema):
275
# Implement validation logic
276
return True
277
278
# Usage in handlers
279
@enroute.broker.command("user.create")
280
async def create_user(request: Request) -> Response:
281
# Wrap request with validation
282
schema = {"type": "object", "required": ["email", "name"]}
283
validated_request = ValidatedRequest(request, schema)
284
285
try:
286
user_data = await validated_request.content()
287
# Process creation
288
return Response({"created": True})
289
except ValueError as e:
290
return Response({"error": str(e)}, status=400)
291
```
292
293
### Response Builders and Factories
294
295
```python
296
class ResponseBuilder:
297
def __init__(self):
298
self._data = None
299
self._status = 200
300
self._headers = {}
301
302
def with_data(self, data: Any):
303
self._data = data
304
return self
305
306
def with_status(self, status: int):
307
self._status = status
308
return self
309
310
def with_header(self, key: str, value: str):
311
self._headers[key] = value
312
return self
313
314
def build(self) -> Response:
315
response = Response(self._data, status=self._status)
316
# Custom response with headers would need HttpResponse
317
return response
318
319
# Factory methods for common responses
320
class ResponseFactory:
321
@staticmethod
322
def success(data: Any = None) -> Response:
323
return Response(data, status=200)
324
325
@staticmethod
326
def created(data: Any = None) -> Response:
327
return Response(data, status=201)
328
329
@staticmethod
330
def not_found(message: str = "Not found") -> Response:
331
return Response({"error": message}, status=404)
332
333
@staticmethod
334
def bad_request(message: str = "Bad request") -> Response:
335
return Response({"error": message}, status=400)
336
337
# Usage
338
@enroute.rest.command("/users", method="POST")
339
async def create_user(request: Request) -> Response:
340
try:
341
user_data = await request.content()
342
new_user = create_user_logic(user_data)
343
return ResponseFactory.created(new_user)
344
except ValueError:
345
return ResponseFactory.bad_request("Invalid user data")
346
```
347
348
### Request Processing Pipelines
349
350
```python
351
class RequestPipeline:
352
def __init__(self):
353
self.middlewares = []
354
355
def add_middleware(self, middleware_func):
356
self.middlewares.append(middleware_func)
357
return self
358
359
async def process(self, request: Request) -> Request:
360
current_request = request
361
for middleware in self.middlewares:
362
current_request = await middleware(current_request)
363
return current_request
364
365
# Middleware functions
366
async def auth_middleware(request: Request) -> Request:
367
# Add user authentication
368
if hasattr(request, 'headers'):
369
auth_header = request.headers.get("Authorization")
370
if auth_header:
371
user = authenticate_user(auth_header)
372
REQUEST_USER_CONTEXT_VAR.set(user)
373
return request
374
375
async def logging_middleware(request: Request) -> Request:
376
print(f"Processing request: {type(request)}")
377
return LoggingRequest(request)
378
379
# Usage in handlers
380
pipeline = RequestPipeline()
381
pipeline.add_middleware(auth_middleware)
382
pipeline.add_middleware(logging_middleware)
383
384
@enroute.broker.command("user.update")
385
async def update_user(request: Request) -> Response:
386
# Process request through pipeline
387
processed_request = await pipeline.process(request)
388
389
# Handle the processed request
390
user_data = await processed_request.content()
391
return Response({"updated": True})
392
```