0
# Request and Response Handling
1
2
FastAPI provides comprehensive request and response objects for accessing HTTP request data and creating custom responses. The Request object gives access to all request information, while Response classes allow fine-grained control over HTTP responses.
3
4
## Capabilities
5
6
### Request Object
7
8
Object for accessing all HTTP request information including headers, body, query parameters, and connection details.
9
10
```python { .api }
11
class Request:
12
"""
13
HTTP request object providing access to all request data.
14
15
Attributes (read-only):
16
- method: HTTP method (GET, POST, etc.)
17
- url: Complete URL object with components
18
- headers: HTTP headers as case-insensitive mapping
19
- query_params: Query parameters as mapping
20
- path_params: Path parameters extracted from URL pattern
21
- cookies: HTTP cookies as mapping
22
- client: Client connection information (host, port)
23
- session: Session data (if session middleware is enabled)
24
- state: Application state for storing data across middleware
25
"""
26
27
method: str
28
url: URL
29
headers: Headers
30
query_params: QueryParams
31
path_params: Dict[str, Any]
32
cookies: Dict[str, str]
33
client: Optional[Address]
34
session: Dict[str, Any]
35
state: State
36
37
async def body(self) -> bytes:
38
"""
39
Get raw request body as bytes.
40
41
Returns:
42
Complete request body as bytes
43
44
Note: Can only be called once per request
45
"""
46
47
async def json(self) -> Any:
48
"""
49
Parse request body as JSON.
50
51
Returns:
52
Parsed JSON data (dict, list, or primitive)
53
54
Raises:
55
JSONDecodeError: If body is not valid JSON
56
"""
57
58
async def form(self) -> FormData:
59
"""
60
Parse request body as form data.
61
62
Returns:
63
FormData object with form fields and files
64
65
Note: Handles both URL-encoded and multipart form data
66
"""
67
68
def stream(self) -> AsyncGenerator[bytes, None]:
69
"""
70
Get request body as async stream.
71
72
Returns:
73
Async generator yielding body chunks
74
75
Note: Useful for large request bodies
76
"""
77
78
def scope(self) -> Dict[str, Any]:
79
"""
80
Get ASGI scope dictionary.
81
82
Returns:
83
Complete ASGI scope with request metadata
84
"""
85
```
86
87
### Response Classes
88
89
Classes for creating HTTP responses with control over content, headers, status codes, and media types.
90
91
```python { .api }
92
class Response:
93
"""
94
Basic HTTP response class.
95
"""
96
def __init__(
97
self,
98
content: Any = None,
99
status_code: int = 200,
100
headers: Optional[Dict[str, str]] = None,
101
media_type: Optional[str] = None,
102
background: Optional[BackgroundTask] = None
103
):
104
"""
105
Create HTTP response.
106
107
Parameters:
108
- content: Response content (string, bytes, or iterable)
109
- status_code: HTTP status code
110
- headers: Additional response headers
111
- media_type: Content-Type header value
112
- background: Background task to run after response
113
"""
114
115
class JSONResponse(Response):
116
"""
117
JSON response with automatic serialization.
118
"""
119
def __init__(
120
self,
121
content: Any = None,
122
status_code: int = 200,
123
headers: Optional[Dict[str, str]] = None,
124
media_type: str = "application/json",
125
background: Optional[BackgroundTask] = None
126
):
127
"""
128
Create JSON response with automatic serialization.
129
130
Parameters:
131
- content: Python object to serialize as JSON
132
- status_code: HTTP status code
133
- headers: Additional response headers
134
- media_type: Content-Type (defaults to application/json)
135
- background: Background task to run after response
136
"""
137
138
class HTMLResponse(Response):
139
"""
140
HTML response for serving HTML content.
141
"""
142
def __init__(
143
self,
144
content: str = "",
145
status_code: int = 200,
146
headers: Optional[Dict[str, str]] = None,
147
media_type: str = "text/html",
148
background: Optional[BackgroundTask] = None
149
):
150
"""
151
Create HTML response.
152
153
Parameters:
154
- content: HTML content as string
155
- status_code: HTTP status code
156
- headers: Additional response headers
157
- media_type: Content-Type (defaults to text/html)
158
- background: Background task to run after response
159
"""
160
161
class PlainTextResponse(Response):
162
"""
163
Plain text response.
164
"""
165
def __init__(
166
self,
167
content: str = "",
168
status_code: int = 200,
169
headers: Optional[Dict[str, str]] = None,
170
media_type: str = "text/plain",
171
background: Optional[BackgroundTask] = None
172
):
173
"""
174
Create plain text response.
175
176
Parameters:
177
- content: Plain text content
178
- status_code: HTTP status code
179
- headers: Additional response headers
180
- media_type: Content-Type (defaults to text/plain)
181
- background: Background task to run after response
182
"""
183
184
class RedirectResponse(Response):
185
"""
186
HTTP redirect response.
187
"""
188
def __init__(
189
self,
190
url: Union[str, URL],
191
status_code: int = 307,
192
headers: Optional[Dict[str, str]] = None,
193
background: Optional[BackgroundTask] = None
194
):
195
"""
196
Create redirect response.
197
198
Parameters:
199
- url: Redirect target URL
200
- status_code: HTTP redirect status code (301, 302, 307, 308)
201
- headers: Additional response headers
202
- background: Background task to run after response
203
"""
204
205
class StreamingResponse(Response):
206
"""
207
Streaming response for large or generated content.
208
"""
209
def __init__(
210
self,
211
content: Union[Iterable[Union[str, bytes]], AsyncIterable[Union[str, bytes]]],
212
status_code: int = 200,
213
headers: Optional[Dict[str, str]] = None,
214
media_type: Optional[str] = None,
215
background: Optional[BackgroundTask] = None
216
):
217
"""
218
Create streaming response.
219
220
Parameters:
221
- content: Iterable or async iterable of content chunks
222
- status_code: HTTP status code
223
- headers: Additional response headers
224
- media_type: Content-Type header value
225
- background: Background task to run after response
226
"""
227
228
class FileResponse(Response):
229
"""
230
File download response.
231
"""
232
def __init__(
233
self,
234
path: Union[str, os.PathLike],
235
status_code: int = 200,
236
headers: Optional[Dict[str, str]] = None,
237
media_type: Optional[str] = None,
238
background: Optional[BackgroundTask] = None,
239
filename: Optional[str] = None,
240
stat_result: Optional[os.stat_result] = None,
241
method: Optional[str] = None
242
):
243
"""
244
Create file download response.
245
246
Parameters:
247
- path: File system path to file
248
- status_code: HTTP status code
249
- headers: Additional response headers
250
- media_type: Content-Type (automatically detected if None)
251
- background: Background task to run after response
252
- filename: Filename for Content-Disposition header
253
- stat_result: Pre-computed file stat result
254
- method: HTTP method for conditional response
255
"""
256
```
257
258
### High-Performance Response Classes
259
260
FastAPI-specific response classes optimized for performance.
261
262
```python { .api }
263
class UJSONResponse(JSONResponse):
264
"""
265
High-performance JSON response using ujson library.
266
267
Note: Requires 'ujson' package to be installed
268
Provides faster JSON serialization than standard library
269
"""
270
271
class ORJSONResponse(JSONResponse):
272
"""
273
High-performance JSON response using orjson library.
274
275
Note: Requires 'orjson' package to be installed
276
Provides fastest JSON serialization with additional features
277
"""
278
```
279
280
### Data Structure Classes
281
282
Classes for structured access to request data components.
283
284
```python { .api }
285
class Headers:
286
"""
287
Case-insensitive HTTP headers mapping.
288
289
Supports:
290
- Case-insensitive key access
291
- Multiple values per header
292
- Standard dict-like interface
293
"""
294
295
class QueryParams:
296
"""
297
URL query parameters mapping.
298
299
Supports:
300
- Multiple values per parameter
301
- Automatic type conversion
302
- Standard dict-like interface
303
"""
304
305
class FormData:
306
"""
307
Form data container for form submissions.
308
309
Supports:
310
- Form fields as key-value pairs
311
- File uploads as UploadFile objects
312
- Multiple values per field name
313
"""
314
315
class URL:
316
"""
317
URL manipulation and component access.
318
319
Attributes:
320
- scheme: URL scheme (http, https)
321
- hostname: Hostname or IP address
322
- port: Port number
323
- path: URL path
324
- query: Query string
325
- fragment: URL fragment
326
"""
327
328
class Address:
329
"""
330
Network address representation.
331
332
Attributes:
333
- host: IP address or hostname
334
- port: Port number
335
"""
336
```
337
338
## Usage Examples
339
340
### Accessing Request Information
341
342
```python
343
from fastapi import FastAPI, Request
344
from typing import Dict, Any
345
346
app = FastAPI()
347
348
@app.get("/request-info")
349
async def get_request_info(request: Request):
350
return {
351
"method": request.method,
352
"url": str(request.url),
353
"base_url": str(request.base_url),
354
"headers": dict(request.headers),
355
"query_params": dict(request.query_params),
356
"path_params": request.path_params,
357
"cookies": request.cookies,
358
"client_host": request.client.host if request.client else None,
359
"user_agent": request.headers.get("user-agent")
360
}
361
362
@app.post("/inspect-body")
363
async def inspect_body(request: Request):
364
# Access raw body
365
body = await request.body()
366
367
# Try to parse as JSON
368
try:
369
json_data = await request.json()
370
except:
371
json_data = None
372
373
return {
374
"body_length": len(body),
375
"body_preview": body[:100].decode("utf-8", errors="ignore"),
376
"json_data": json_data,
377
"content_type": request.headers.get("content-type")
378
}
379
```
380
381
### Custom Response Classes
382
383
```python
384
from fastapi import FastAPI
385
from fastapi.responses import HTMLResponse, JSONResponse, PlainTextResponse, RedirectResponse
386
387
app = FastAPI()
388
389
@app.get("/html", response_class=HTMLResponse)
390
async def get_html():
391
html_content = """
392
<html>
393
<head>
394
<title>FastAPI HTML Response</title>
395
</head>
396
<body>
397
<h1>Hello HTML!</h1>
398
<p>This is an HTML response from FastAPI.</p>
399
</body>
400
</html>
401
"""
402
return html_content
403
404
@app.get("/plain-text", response_class=PlainTextResponse)
405
async def get_plain_text():
406
return "This is plain text response"
407
408
@app.get("/custom-json")
409
async def get_custom_json():
410
return JSONResponse(
411
content={"message": "Custom JSON response"},
412
status_code=201,
413
headers={"X-Custom-Header": "custom-value"}
414
)
415
416
@app.get("/redirect-example")
417
async def redirect_example():
418
return RedirectResponse(url="/new-location", status_code=302)
419
```
420
421
### Streaming Responses
422
423
```python
424
from fastapi import FastAPI
425
from fastapi.responses import StreamingResponse
426
import io
427
import json
428
import time
429
430
app = FastAPI()
431
432
@app.get("/stream-data")
433
async def stream_data():
434
def generate_data():
435
for i in range(100):
436
data = {"item": i, "timestamp": time.time()}
437
yield f"data: {json.dumps(data)}\n"
438
time.sleep(0.1) # Simulate processing delay
439
440
return StreamingResponse(
441
generate_data(),
442
media_type="text/plain",
443
headers={"X-Stream-Type": "data-stream"}
444
)
445
446
@app.get("/stream-csv")
447
async def stream_csv():
448
def generate_csv():
449
yield "id,name,value\n"
450
for i in range(1000):
451
yield f"{i},item_{i},{i*10}\n"
452
453
return StreamingResponse(
454
generate_csv(),
455
media_type="text/csv",
456
headers={"Content-Disposition": "attachment; filename=data.csv"}
457
)
458
459
@app.get("/stream-async")
460
async def stream_async():
461
async def generate_async_data():
462
for i in range(50):
463
data = f"Async chunk {i}\n"
464
yield data.encode()
465
# Simulate async operation
466
await asyncio.sleep(0.05)
467
468
return StreamingResponse(
469
generate_async_data(),
470
media_type="text/plain"
471
)
472
```
473
474
### File Responses
475
476
```python
477
from fastapi import FastAPI, HTTPException
478
from fastapi.responses import FileResponse
479
import os
480
481
app = FastAPI()
482
483
@app.get("/download/{filename}")
484
async def download_file(filename: str):
485
file_path = f"/path/to/files/{filename}"
486
487
if not os.path.exists(file_path):
488
raise HTTPException(status_code=404, detail="File not found")
489
490
return FileResponse(
491
path=file_path,
492
filename=filename,
493
media_type="application/octet-stream"
494
)
495
496
@app.get("/download-with-name/{file_id}")
497
async def download_with_custom_name(file_id: int):
498
# Look up file by ID
499
file_info = get_file_info(file_id) # Your database lookup
500
501
return FileResponse(
502
path=file_info["path"],
503
filename=f"report_{file_id}.pdf",
504
media_type="application/pdf",
505
headers={"X-File-ID": str(file_id)}
506
)
507
```
508
509
### Form Data Handling
510
511
```python
512
from fastapi import FastAPI, Request, Form, File, UploadFile
513
from fastapi.responses import HTMLResponse
514
515
app = FastAPI()
516
517
@app.get("/form", response_class=HTMLResponse)
518
async def show_form():
519
return """
520
<html>
521
<body>
522
<form action="/submit-form" method="post" enctype="multipart/form-data">
523
<input type="text" name="username" placeholder="Username">
524
<input type="email" name="email" placeholder="Email">
525
<input type="file" name="file">
526
<input type="submit" value="Submit">
527
</form>
528
</body>
529
</html>
530
"""
531
532
@app.post("/submit-form")
533
async def submit_form(request: Request):
534
# Access form data through request
535
form = await request.form()
536
537
return {
538
"form_data": {
539
"username": form.get("username"),
540
"email": form.get("email"),
541
"file": {
542
"filename": form["file"].filename if "file" in form else None,
543
"content_type": form["file"].content_type if "file" in form else None
544
}
545
},
546
"all_fields": list(form.keys())
547
}
548
549
@app.post("/submit-form-params")
550
async def submit_form_with_params(
551
username: str = Form(...),
552
email: str = Form(...),
553
file: UploadFile = File(...)
554
):
555
# Access form data through parameter injection
556
return {
557
"username": username,
558
"email": email,
559
"file": {
560
"filename": file.filename,
561
"content_type": file.content_type,
562
"size": len(await file.read())
563
}
564
}
565
```
566
567
### Advanced Request Processing
568
569
```python
570
from fastapi import FastAPI, Request, HTTPException
571
from fastapi.responses import JSONResponse
572
import json
573
574
app = FastAPI()
575
576
@app.middleware("http")
577
async def log_requests(request: Request, call_next):
578
# Log request details
579
print(f"Request: {request.method} {request.url}")
580
print(f"Headers: {dict(request.headers)}")
581
582
response = await call_next(request)
583
584
print(f"Response: {response.status_code}")
585
return response
586
587
@app.post("/webhook")
588
async def handle_webhook(request: Request):
589
# Verify content type
590
content_type = request.headers.get("content-type")
591
if content_type != "application/json":
592
raise HTTPException(status_code=400, detail="Content-Type must be application/json")
593
594
# Verify webhook signature (example)
595
signature = request.headers.get("x-webhook-signature")
596
if not signature:
597
raise HTTPException(status_code=400, detail="Missing webhook signature")
598
599
# Get raw body for signature verification
600
body = await request.body()
601
602
# Verify signature (simplified example)
603
expected_signature = compute_signature(body) # Your signature logic
604
if signature != expected_signature:
605
raise HTTPException(status_code=401, detail="Invalid signature")
606
607
# Parse JSON payload
608
try:
609
payload = json.loads(body)
610
except json.JSONDecodeError:
611
raise HTTPException(status_code=400, detail="Invalid JSON")
612
613
# Process webhook
614
result = process_webhook(payload) # Your processing logic
615
616
return JSONResponse(
617
content={"status": "processed", "result": result},
618
headers={"X-Webhook-ID": payload.get("id", "unknown")}
619
)
620
621
@app.get("/client-info")
622
async def get_client_info(request: Request):
623
return {
624
"client": {
625
"host": request.client.host if request.client else None,
626
"port": request.client.port if request.client else None
627
},
628
"forwarded_for": request.headers.get("x-forwarded-for"),
629
"real_ip": request.headers.get("x-real-ip"),
630
"user_agent": request.headers.get("user-agent"),
631
"accept": request.headers.get("accept"),
632
"accept_language": request.headers.get("accept-language"),
633
"referer": request.headers.get("referer")
634
}
635
```
636
637
### Response Customization
638
639
```python
640
from fastapi import FastAPI, Response, status
641
from fastapi.responses import JSONResponse
642
from datetime import datetime
643
644
app = FastAPI()
645
646
@app.get("/custom-headers")
647
async def custom_headers(response: Response):
648
# Modify response directly
649
response.headers["X-Custom-Header"] = "custom-value"
650
response.headers["X-Timestamp"] = str(datetime.now())
651
response.status_code = status.HTTP_200_OK
652
653
return {"message": "Response with custom headers"}
654
655
@app.post("/conditional-response")
656
async def conditional_response(data: dict):
657
if "error" in data:
658
return JSONResponse(
659
status_code=400,
660
content={"error": data["error"], "timestamp": str(datetime.now())},
661
headers={"X-Error-Type": "user-error"}
662
)
663
664
return JSONResponse(
665
status_code=201,
666
content={"result": "success", "data": data},
667
headers={"X-Result-Type": "success"}
668
)
669
670
@app.get("/cache-control")
671
async def cache_control():
672
return JSONResponse(
673
content={"data": "cacheable content"},
674
headers={
675
"Cache-Control": "public, max-age=3600",
676
"ETag": "unique-etag-value",
677
"Last-Modified": "Wed, 21 Oct 2023 07:28:00 GMT"
678
}
679
)
680
```