0
# Request and Response Handling
1
2
FastAPI provides comprehensive request and response handling through Starlette's Request and Response objects, along with FastAPI-specific enhancements like UploadFile for file handling and various response classes for different content types.
3
4
## Capabilities
5
6
### Request Object
7
8
HTTP request object providing access to all request data including headers, cookies, query parameters, form data, and JSON body.
9
10
```python { .api }
11
class Request:
12
"""
13
Starlette Request object with all HTTP request functionality.
14
15
Key attributes and methods for accessing request data:
16
"""
17
18
# Request metadata
19
method: str # HTTP method (GET, POST, etc.)
20
url: URL # Complete URL object
21
headers: Headers # HTTP headers
22
query_params: QueryParams # Query string parameters
23
path_params: dict # Path parameters from URL
24
cookies: dict # HTTP cookies
25
client: Address # Client connection info
26
27
# Request body access
28
async def body(self) -> bytes:
29
"""Get raw request body as bytes."""
30
pass
31
32
async def json(self) -> Any:
33
"""Parse request body as JSON."""
34
pass
35
36
async def form(self) -> FormData:
37
"""Parse request body as form data."""
38
pass
39
40
# State and context
41
state: State # Request-scoped state storage
42
scope: dict # ASGI scope dictionary
43
44
# Utility methods
45
def url_for(self, name: str, **path_params) -> str:
46
"""Generate URL for named route."""
47
pass
48
```
49
50
### Response Classes
51
52
Base response class and specialized response classes for different content types and use cases.
53
54
```python { .api }
55
class Response:
56
def __init__(
57
self,
58
content: Any = None,
59
status_code: int = 200,
60
headers: dict = None,
61
media_type: str = None,
62
background: BackgroundTask = None,
63
) -> None:
64
"""
65
Base HTTP response class.
66
67
Parameters:
68
- content: Response content (string, bytes, or None)
69
- status_code: HTTP status code
70
- headers: Additional HTTP headers
71
- media_type: Content-Type header value
72
- background: Background task to run after response
73
"""
74
75
# Response properties
76
status_code: int # HTTP status code
77
headers: Headers # Response headers
78
media_type: str # Content-Type
79
body: bytes # Response body
80
background: BackgroundTask # Background task
81
82
class JSONResponse(Response):
83
def __init__(
84
self,
85
content: Any = None,
86
status_code: int = 200,
87
headers: dict = None,
88
media_type: str = "application/json",
89
background: BackgroundTask = None,
90
) -> None:
91
"""
92
JSON response with automatic serialization.
93
94
Parameters:
95
- content: Python object to serialize as JSON
96
- Other parameters same as Response
97
"""
98
99
class HTMLResponse(Response):
100
def __init__(
101
self,
102
content: str = None,
103
status_code: int = 200,
104
headers: dict = None,
105
media_type: str = "text/html",
106
background: BackgroundTask = None,
107
) -> None:
108
"""HTML response for returning HTML content."""
109
110
class PlainTextResponse(Response):
111
def __init__(
112
self,
113
content: str = None,
114
status_code: int = 200,
115
headers: dict = None,
116
media_type: str = "text/plain",
117
background: BackgroundTask = None,
118
) -> None:
119
"""Plain text response."""
120
121
class RedirectResponse(Response):
122
def __init__(
123
self,
124
url: str,
125
status_code: int = 307,
126
headers: dict = None,
127
background: BackgroundTask = None,
128
) -> None:
129
"""
130
HTTP redirect response.
131
132
Parameters:
133
- url: Target URL for redirection
134
- status_code: Redirect status code (307, 302, 301, etc.)
135
"""
136
137
class FileResponse(Response):
138
def __init__(
139
self,
140
path: str = None,
141
status_code: int = 200,
142
headers: dict = None,
143
media_type: str = None,
144
background: BackgroundTask = None,
145
filename: str = None,
146
stat_result: os.stat_result = None,
147
method: str = None,
148
) -> None:
149
"""
150
File download response.
151
152
Parameters:
153
- path: File system path to serve
154
- filename: Filename for Content-Disposition header
155
- stat_result: Cached file stat result for performance
156
- method: HTTP method for conditional logic
157
"""
158
159
class StreamingResponse(Response):
160
def __init__(
161
self,
162
content: Any,
163
status_code: int = 200,
164
headers: dict = None,
165
media_type: str = None,
166
background: BackgroundTask = None,
167
) -> None:
168
"""
169
Streaming response for large data or real-time content.
170
171
Parameters:
172
- content: Iterable or async iterable of bytes/strings
173
- Other parameters same as Response
174
"""
175
```
176
177
### FastAPI-Specific Response Classes
178
179
Enhanced JSON response classes using high-performance JSON libraries.
180
181
```python { .api }
182
class UJSONResponse(JSONResponse):
183
def render(self, content: Any) -> bytes:
184
"""
185
Ultra-fast JSON response using ujson library.
186
187
Requires: pip install ujson
188
Provides faster JSON serialization than standard library.
189
"""
190
191
class ORJSONResponse(JSONResponse):
192
def render(self, content: Any) -> bytes:
193
"""
194
Fast JSON response using orjson library.
195
196
Requires: pip install orjson
197
Fastest JSON serialization with additional features.
198
"""
199
```
200
201
### File Upload Handling
202
203
UploadFile class for handling multipart file uploads with async file operations.
204
205
```python { .api }
206
class UploadFile:
207
def __init__(
208
self,
209
file: BinaryIO,
210
*,
211
size: int = None,
212
filename: str = None,
213
headers: Headers = None,
214
) -> None: ...
215
216
# File metadata
217
filename: Optional[str] # Original filename
218
content_type: Optional[str] # MIME content type
219
headers: Headers # File-specific headers
220
size: Optional[int] # File size in bytes
221
file: BinaryIO # Underlying file object
222
223
# Async file operations
224
async def read(self, size: int = -1) -> bytes:
225
"""Read data from uploaded file."""
226
227
async def readline(self, size: int = -1) -> bytes:
228
"""Read a line from uploaded file."""
229
230
async def readlines(self) -> List[bytes]:
231
"""Read all lines from uploaded file."""
232
233
async def write(self, data: bytes) -> None:
234
"""Write data to uploaded file."""
235
236
async def seek(self, offset: int) -> None:
237
"""Seek to position in uploaded file."""
238
239
async def close(self) -> None:
240
"""Close the uploaded file."""
241
```
242
243
## Usage Examples
244
245
### Accessing Request Data
246
247
```python
248
from fastapi import FastAPI, Request
249
250
app = FastAPI()
251
252
@app.post("/analyze-request")
253
async def analyze_request(request: Request):
254
return {
255
"method": request.method,
256
"url": str(request.url),
257
"headers": dict(request.headers),
258
"query_params": dict(request.query_params),
259
"path_params": request.path_params,
260
"cookies": request.cookies,
261
"client": f"{request.client.host}:{request.client.port}",
262
"body": (await request.body()).decode()
263
}
264
265
@app.get("/items/{item_id}")
266
async def get_item(item_id: int, request: Request):
267
# Access path parameters
268
path_params = request.path_params
269
270
# Access query parameters
271
query_params = request.query_params
272
273
# Access headers
274
user_agent = request.headers.get("user-agent")
275
276
return {
277
"item_id": item_id,
278
"path_params": path_params,
279
"query_params": dict(query_params),
280
"user_agent": user_agent
281
}
282
```
283
284
### Custom Response Types
285
286
```python
287
from fastapi import FastAPI
288
from fastapi.responses import HTMLResponse, PlainTextResponse, RedirectResponse
289
290
app = FastAPI()
291
292
@app.get("/html", response_class=HTMLResponse)
293
def get_html():
294
html_content = """
295
<html>
296
<head><title>FastAPI HTML</title></head>
297
<body><h1>Hello, HTML!</h1></body>
298
</html>
299
"""
300
return html_content
301
302
@app.get("/text", response_class=PlainTextResponse)
303
def get_text():
304
return "Hello, plain text!"
305
306
@app.get("/redirect")
307
def redirect_to_docs():
308
return RedirectResponse(url="/docs")
309
310
@app.get("/permanent-redirect")
311
def permanent_redirect():
312
return RedirectResponse(url="/new-location", status_code=301)
313
```
314
315
### File Downloads
316
317
```python
318
from fastapi import FastAPI
319
from fastapi.responses import FileResponse
320
import tempfile
321
import os
322
323
app = FastAPI()
324
325
@app.get("/download-file")
326
def download_file():
327
# Create a temporary file
328
with tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".txt") as f:
329
f.write("This is a downloadable file content.")
330
temp_file_path = f.name
331
332
return FileResponse(
333
path=temp_file_path,
334
filename="download.txt",
335
media_type="text/plain"
336
)
337
338
@app.get("/download-image")
339
def download_image():
340
# Serve an existing file
341
file_path = "/path/to/image.jpg"
342
return FileResponse(
343
path=file_path,
344
filename="image.jpg",
345
media_type="image/jpeg"
346
)
347
```
348
349
### Streaming Responses
350
351
```python
352
from fastapi import FastAPI
353
from fastapi.responses import StreamingResponse
354
import io
355
import json
356
357
app = FastAPI()
358
359
@app.get("/stream-data")
360
def stream_data():
361
def generate_data():
362
for i in range(1000):
363
data = {"item": i, "value": f"data_{i}"}
364
yield f"data: {json.dumps(data)}\n"
365
366
return StreamingResponse(
367
generate_data(),
368
media_type="text/plain"
369
)
370
371
@app.get("/stream-csv")
372
def stream_csv():
373
def generate_csv():
374
yield "id,name,email\n"
375
for i in range(1000):
376
yield f"{i},user_{i},user_{i}@example.com\n"
377
378
return StreamingResponse(
379
generate_csv(),
380
media_type="text/csv",
381
headers={"Content-Disposition": "attachment; filename=users.csv"}
382
)
383
384
@app.get("/stream-file")
385
def stream_large_file():
386
def iterfile(file_path: str):
387
with open(file_path, mode="rb") as file_like:
388
while True:
389
chunk = file_like.read(1024)
390
if not chunk:
391
break
392
yield chunk
393
394
return StreamingResponse(
395
iterfile("/path/to/large/file.bin"),
396
media_type="application/octet-stream"
397
)
398
```
399
400
### File Upload Handling
401
402
```python
403
from fastapi import FastAPI, File, UploadFile, HTTPException
404
from typing import List
405
import aiofiles
406
import os
407
408
app = FastAPI()
409
410
@app.post("/upload-file/")
411
async def upload_file(file: UploadFile = File(...)):
412
# Validate file type
413
if not file.content_type.startswith("image/"):
414
raise HTTPException(400, "File must be an image")
415
416
# Read file content
417
content = await file.read()
418
419
# Save file
420
file_path = f"uploads/{file.filename}"
421
async with aiofiles.open(file_path, "wb") as f:
422
await f.write(content)
423
424
return {
425
"filename": file.filename,
426
"content_type": file.content_type,
427
"size": len(content),
428
"saved_to": file_path
429
}
430
431
@app.post("/upload-multiple/")
432
async def upload_multiple_files(files: List[UploadFile] = File(...)):
433
uploaded_files = []
434
435
for file in files:
436
content = await file.read()
437
file_info = {
438
"filename": file.filename,
439
"content_type": file.content_type,
440
"size": len(content)
441
}
442
uploaded_files.append(file_info)
443
444
# Reset file pointer if you need to read again
445
await file.seek(0)
446
447
return {"uploaded_files": uploaded_files}
448
449
@app.post("/process-file/")
450
async def process_file(file: UploadFile = File(...)):
451
# Process file line by line for large files
452
processed_lines = []
453
454
# Read file line by line
455
content = await file.read()
456
lines = content.decode().split('\n')
457
458
for i, line in enumerate(lines):
459
if line.strip(): # Skip empty lines
460
processed_lines.append(f"Line {i+1}: {line.strip()}")
461
462
await file.close()
463
464
return {
465
"filename": file.filename,
466
"total_lines": len(processed_lines),
467
"processed_lines": processed_lines[:10] # Return first 10 lines
468
}
469
```
470
471
### Custom Response with Headers
472
473
```python
474
from fastapi import FastAPI, Response
475
from fastapi.responses import JSONResponse
476
477
app = FastAPI()
478
479
@app.get("/custom-headers")
480
def custom_headers():
481
content = {"message": "Custom headers response"}
482
headers = {
483
"X-Custom-Header": "Custom Value",
484
"X-Processing-Time": "0.123",
485
"Cache-Control": "no-cache"
486
}
487
return JSONResponse(content=content, headers=headers)
488
489
@app.get("/set-cookie")
490
def set_cookie(response: Response):
491
content = {"message": "Cookie set"}
492
response.set_cookie(
493
key="session_id",
494
value="abc123",
495
max_age=3600,
496
httponly=True,
497
secure=True,
498
samesite="lax"
499
)
500
return content
501
502
@app.get("/custom-status")
503
def custom_status():
504
return JSONResponse(
505
content={"message": "Created successfully"},
506
status_code=201,
507
headers={"Location": "/items/123"}
508
)
509
```
510
511
### Response Model with Custom Response Class
512
513
```python
514
from fastapi import FastAPI
515
from fastapi.responses import ORJSONResponse
516
from pydantic import BaseModel
517
from typing import List
518
519
app = FastAPI(default_response_class=ORJSONResponse)
520
521
class Item(BaseModel):
522
id: int
523
name: str
524
price: float
525
526
@app.get("/items", response_model=List[Item])
527
def get_items():
528
# FastAPI will use ORJSONResponse for serialization
529
return [
530
{"id": 1, "name": "Item 1", "price": 10.5},
531
{"id": 2, "name": "Item 2", "price": 20.0}
532
]
533
534
@app.get("/custom-json", response_class=ORJSONResponse)
535
def get_custom_json():
536
# Explicitly use ORJSONResponse
537
return {"message": "Fast JSON response"}
538
```