0
# Response System
1
2
Various response classes for different content types and HTTP behaviors, including HTML responses, JSON responses, template responses, file downloads, and redirects. The response system provides a flexible way to return different types of content from views.
3
4
## Capabilities
5
6
### Base Response Classes
7
8
Foundation response classes that all other responses inherit from, providing basic HTTP response functionality.
9
10
```python { .api }
11
class AbstractResponse:
12
"""Abstract base class for all response types."""
13
def __init__(self):
14
"""Initialize abstract response."""
15
16
# Properties
17
interactive: bool # Whether response can be used in interactive views
18
19
class Response(AbstractResponse):
20
def __init__(self, content: str = '', status: int = 200, headers: dict = None,
21
content_type: str = 'text/plain'):
22
"""
23
Create a basic HTTP response.
24
25
Args:
26
content (str): Response content
27
status (int): HTTP status code
28
headers (dict): HTTP headers
29
content_type (str): Content-Type header
30
"""
31
32
# Properties
33
content: str # Response content
34
status: int # HTTP status code
35
headers: dict # HTTP headers
36
content_type: str # Content-Type header
37
interactive: bool # Whether response can be used in interactive views
38
```
39
40
### HTML Responses
41
42
Responses for returning HTML content to the browser.
43
44
```python { .api }
45
class HtmlResponse(Response):
46
def __init__(self, html, status: int = 200, headers: dict = None):
47
"""
48
Create an HTML response.
49
50
Args:
51
html: HTML content (Node objects or string)
52
status (int): HTTP status code
53
headers (dict): HTTP headers
54
"""
55
56
class TemplateResponse(Response):
57
def __init__(self, template_name: str, context: dict = None,
58
status: int = 200, headers: dict = None):
59
"""
60
Create a template-based response.
61
62
Args:
63
template_name (str): Name of template to render
64
context (dict): Template context variables
65
status (int): HTTP status code
66
headers (dict): HTTP headers
67
"""
68
69
class TemplateStringResponse(Response):
70
def __init__(self, template_string: str, context: dict = None,
71
status: int = 200, headers: dict = None):
72
"""
73
Create a response from template string.
74
75
Args:
76
template_string (str): Template content as string
77
context (dict): Template context variables
78
status (int): HTTP status code
79
headers (dict): HTTP headers
80
"""
81
```
82
83
#### HTML Response Usage Example
84
85
```python
86
from lona import App, View
87
from lona.html import HTML, H1, P
88
from lona.responses import HtmlResponse, TemplateResponse
89
90
app = App(__file__)
91
92
@app.route('/simple')
93
class SimpleView(View):
94
def handle_request(self, request):
95
html = HTML(
96
H1('Simple Response'),
97
P('This is a simple HTML response.')
98
)
99
return HtmlResponse(html)
100
101
@app.route('/template')
102
class TemplateView(View):
103
def handle_request(self, request):
104
context = {
105
'title': 'Template Example',
106
'username': request.user.username if request.user else 'Guest'
107
}
108
return TemplateResponse('base.html', context)
109
```
110
111
### Data Responses
112
113
Responses for returning structured data in various formats.
114
115
```python { .api }
116
class JsonResponse(Response):
117
def __init__(self, json_data, status: int = 200, headers: dict = None):
118
"""
119
Create a JSON response.
120
121
Args:
122
json_data: Data to serialize as JSON
123
status (int): HTTP status code
124
headers (dict): HTTP headers
125
"""
126
```
127
128
#### JSON Response Usage Example
129
130
```python
131
from lona.responses import JsonResponse
132
133
@app.route('/api/users')
134
class UserApiView(View):
135
def handle_request(self, request):
136
users = [
137
{'id': 1, 'name': 'John', 'email': 'john@example.com'},
138
{'id': 2, 'name': 'Jane', 'email': 'jane@example.com'}
139
]
140
return JsonResponse({
141
'users': users,
142
'count': len(users),
143
'status': 'success'
144
})
145
146
@app.route('/api/user/<int:user_id>')
147
class SingleUserApiView(View):
148
def handle_request(self, request):
149
user_id = request.match_info['user_id']
150
151
if user_id == 404:
152
return JsonResponse(
153
{'error': 'User not found'},
154
status=404
155
)
156
157
return JsonResponse({
158
'id': user_id,
159
'name': f'User {user_id}',
160
'active': True
161
})
162
```
163
164
### File Responses
165
166
Responses for serving files and downloads.
167
168
```python { .api }
169
class FileResponse(Response):
170
def __init__(self, path: str, content_type: str = None,
171
as_attachment: bool = False, headers: dict = None):
172
"""
173
Create a file download response.
174
175
Args:
176
path (str): Path to file on filesystem
177
content_type (str): MIME type (auto-detected if None)
178
as_attachment (bool): Force download vs inline display
179
headers (dict): Additional HTTP headers
180
"""
181
182
# Properties
183
path: str # File system path
184
as_attachment: bool # Download behavior flag
185
```
186
187
#### File Response Usage Example
188
189
```python
190
import os
191
from lona.responses import FileResponse
192
193
@app.route('/download/<filename>')
194
class DownloadView(View):
195
def handle_request(self, request):
196
filename = request.match_info['filename']
197
file_path = os.path.join('uploads', filename)
198
199
if not os.path.exists(file_path):
200
return JsonResponse({'error': 'File not found'}, status=404)
201
202
return FileResponse(
203
path=file_path,
204
as_attachment=True, # Force download
205
headers={'X-Download-Source': 'lona-app'}
206
)
207
208
@app.route('/image/<filename>')
209
class ImageView(View):
210
def handle_request(self, request):
211
filename = request.match_info['filename']
212
image_path = os.path.join('static', 'images', filename)
213
214
return FileResponse(
215
path=image_path,
216
content_type='image/jpeg',
217
as_attachment=False # Display inline
218
)
219
```
220
221
### Redirect Responses
222
223
Responses for redirecting users to different URLs.
224
225
```python { .api }
226
class RedirectResponse(Response):
227
def __init__(self, redirect_url: str):
228
"""
229
Create an internal redirect response.
230
231
Args:
232
redirect_url (str): URL to redirect to within the app
233
"""
234
235
class HttpRedirectResponse(Response):
236
def __init__(self, redirect_url: str, status: int = 302):
237
"""
238
Create an HTTP redirect response.
239
240
Args:
241
redirect_url (str): URL to redirect to (can be external)
242
status (int): HTTP redirect status code (301, 302, 303, 307, 308)
243
"""
244
245
# Properties
246
redirect_url: str # Target URL
247
```
248
249
#### Redirect Response Usage Example
250
251
```python
252
from lona.responses import RedirectResponse, HttpRedirectResponse
253
254
@app.route('/login')
255
class LoginView(View):
256
def handle_request(self, request):
257
if request.user:
258
# User already logged in, redirect to dashboard
259
return RedirectResponse('/dashboard')
260
261
# Show login form
262
return self.show_login_form()
263
264
@app.route('/external-redirect')
265
class ExternalRedirectView(View):
266
def handle_request(self, request):
267
# Redirect to external site
268
return HttpRedirectResponse(
269
'https://external-site.com/page',
270
status=301 # Permanent redirect
271
)
272
273
@app.route('/logout')
274
class LogoutView(View):
275
def handle_request(self, request):
276
# Perform logout logic
277
request.session.clear()
278
279
# Redirect to home page
280
return RedirectResponse('/')
281
```
282
283
### Error Responses
284
285
Specialized responses for handling error conditions.
286
287
```python { .api }
288
class ErrorResponse(Response):
289
def __init__(self, message: str, status: int = 500, headers: dict = None):
290
"""
291
Create an error response.
292
293
Args:
294
message (str): Error message
295
status (int): HTTP error status code
296
headers (dict): HTTP headers
297
"""
298
299
class NotFoundResponse(ErrorResponse):
300
def __init__(self, message: str = 'Not Found', headers: dict = None):
301
"""
302
Create a 404 Not Found response.
303
304
Args:
305
message (str): Error message
306
headers (dict): HTTP headers
307
"""
308
309
class ForbiddenResponse(ErrorResponse):
310
def __init__(self, message: str = 'Forbidden', headers: dict = None):
311
"""
312
Create a 403 Forbidden response.
313
314
Args:
315
message (str): Error message
316
headers (dict): HTTP headers
317
"""
318
```
319
320
#### Error Response Usage Example
321
322
```python
323
from lona.responses import NotFoundResponse, ForbiddenResponse, ErrorResponse
324
325
@app.route('/admin/<path:admin_path>')
326
class AdminView(View):
327
def handle_request(self, request):
328
if not request.user or not request.user.is_admin:
329
return ForbiddenResponse('Admin access required')
330
331
admin_path = request.match_info['admin_path']
332
333
if admin_path not in ['users', 'settings', 'logs']:
334
return NotFoundResponse(f'Admin page "{admin_path}" not found')
335
336
try:
337
return self.render_admin_page(admin_path)
338
except Exception as e:
339
return ErrorResponse(
340
f'Internal server error: {str(e)}',
341
status=500
342
)
343
```
344
345
### Response Headers and Cookies
346
347
Utilities for managing HTTP headers and cookies in responses.
348
349
```python { .api }
350
# Response header utilities (available on all response classes)
351
def set_header(self, name: str, value: str):
352
"""Set an HTTP header."""
353
354
def get_header(self, name: str) -> str:
355
"""Get an HTTP header value."""
356
357
def remove_header(self, name: str):
358
"""Remove an HTTP header."""
359
360
def set_cookie(self, name: str, value: str, max_age: int = None,
361
expires: str = None, path: str = '/', domain: str = None,
362
secure: bool = False, httponly: bool = False):
363
"""Set an HTTP cookie."""
364
365
def delete_cookie(self, name: str, path: str = '/', domain: str = None):
366
"""Delete an HTTP cookie."""
367
```
368
369
#### Headers and Cookies Usage Example
370
371
```python
372
from lona.responses import JsonResponse, RedirectResponse
373
374
@app.route('/api/login')
375
class LoginApiView(View):
376
def handle_request(self, request):
377
# Validate credentials...
378
if valid_credentials:
379
response = JsonResponse({'status': 'logged_in'})
380
381
# Set authentication cookie
382
response.set_cookie(
383
'auth_token',
384
'abc123def456',
385
max_age=3600, # 1 hour
386
httponly=True,
387
secure=True
388
)
389
390
# Set custom headers
391
response.set_header('X-Login-Time', '2023-01-01 12:00:00')
392
response.set_header('Cache-Control', 'no-cache')
393
394
return response
395
else:
396
return JsonResponse({'error': 'Invalid credentials'}, status=401)
397
398
@app.route('/logout')
399
class LogoutView(View):
400
def handle_request(self, request):
401
response = RedirectResponse('/')
402
403
# Clear authentication cookie
404
response.delete_cookie('auth_token')
405
406
return response
407
```
408
409
## Types
410
411
```python { .api }
412
from typing import Union, Optional, Dict, Any
413
from lona.html import Node
414
415
# Content types
416
ResponseContent = Union[str, bytes, Node, dict, list]
417
HTMLContent = Union[str, Node, list]
418
JSONContent = Union[dict, list, str, int, float, bool, None]
419
420
# Response types
421
HTTPStatus = int
422
HTTPHeaders = Optional[Dict[str, str]]
423
TemplateContext = Optional[Dict[str, Any]]
424
425
# Cookie types
426
CookieName = str
427
CookieValue = str
428
CookieExpires = Optional[str]
429
CookieMaxAge = Optional[int]
430
```