0
# HTTP Data Structures
1
2
Specialized data structures for handling HTTP-specific data including multi-value dictionaries, headers, file uploads, and accept headers with proper parsing and type conversion. These structures form the foundation for HTTP data processing in Werkzeug.
3
4
## Capabilities
5
6
### MultiDict - Multi-Value Dictionary
7
8
A dictionary-like data structure that can store multiple values for a single key, essential for handling HTTP form data and query parameters.
9
10
```python { .api }
11
class MultiDict:
12
def __init__(self, mapping=None):
13
"""
14
Create a new MultiDict.
15
16
Parameters:
17
- mapping: Initial data (dict, list of tuples, or MultiDict)
18
"""
19
20
def get(self, key, default=None, type=None):
21
"""
22
Get the first value for a key with optional type conversion.
23
24
Parameters:
25
- key: Key to look up
26
- default: Default value if key not found
27
- type: Callable to convert value (e.g., int, float)
28
29
Returns:
30
First value for key, converted by type function if provided
31
"""
32
33
def getlist(self, key, type=None):
34
"""
35
Get all values for a key as a list.
36
37
Parameters:
38
- key: Key to look up
39
- type: Callable to convert each value
40
41
Returns:
42
List of all values for the key
43
"""
44
45
def add(self, key, value):
46
"""
47
Add a new value for a key without replacing existing values.
48
49
Parameters:
50
- key: Key to add value for
51
- value: Value to add
52
"""
53
54
def setlist(self, key, new_list):
55
"""
56
Set multiple values for a key, replacing any existing values.
57
58
Parameters:
59
- key: Key to set values for
60
- new_list: List of values to set
61
"""
62
63
def setlistdefault(self, key, default_list=None):
64
"""
65
Set default values for a key if it doesn't exist.
66
67
Parameters:
68
- key: Key to check/set
69
- default_list: Default list of values
70
71
Returns:
72
List of values for the key
73
"""
74
75
def items(self, multi=False):
76
"""
77
Iterate over items.
78
79
Parameters:
80
- multi: If True, yield all key-value pairs including duplicates
81
82
Yields:
83
Key-value pairs
84
"""
85
86
def lists(self):
87
"""
88
Iterate over keys and their complete list of values.
89
90
Yields:
91
(key, list_of_values) pairs
92
"""
93
94
def keys(self):
95
"""Return an iterator over all keys."""
96
97
def values(self):
98
"""Return an iterator over all values (first value per key)."""
99
100
def listvalues(self):
101
"""Return an iterator over all values (all values including duplicates)."""
102
```
103
104
### ImmutableMultiDict - Immutable Multi-Value Dictionary
105
106
An immutable version of MultiDict that raises exceptions on modification attempts.
107
108
```python { .api }
109
class ImmutableMultiDict(MultiDict):
110
def __init__(self, mapping=None):
111
"""
112
Create a new immutable MultiDict.
113
114
Parameters:
115
- mapping: Initial data (dict, list of tuples, or MultiDict)
116
"""
117
118
def copy(self):
119
"""
120
Create a mutable copy of this MultiDict.
121
122
Returns:
123
New MultiDict with same data
124
"""
125
```
126
127
### CombinedMultiDict - Combined Multi-Value Dictionaries
128
129
Combines multiple MultiDict instances into a single read-only view.
130
131
```python { .api }
132
class CombinedMultiDict:
133
def __init__(self, dicts):
134
"""
135
Create a combined view of multiple MultiDict instances.
136
137
Parameters:
138
- dicts: List of MultiDict instances to combine
139
"""
140
141
def get(self, key, default=None, type=None):
142
"""
143
Get first value for key from any of the combined dicts.
144
145
Parameters:
146
- key: Key to look up
147
- default: Default value if key not found
148
- type: Callable to convert value
149
150
Returns:
151
First value found in any dict
152
"""
153
154
def getlist(self, key, type=None):
155
"""
156
Get all values for key from all combined dicts.
157
158
Parameters:
159
- key: Key to look up
160
- type: Callable to convert each value
161
162
Returns:
163
List of all values from all dicts
164
"""
165
166
def to_dict(self, flat=True):
167
"""
168
Convert to regular dict.
169
170
Parameters:
171
- flat: If True, use only first value per key
172
173
Returns:
174
Regular dict representation
175
"""
176
```
177
178
### Headers - HTTP Headers Collection
179
180
Specialized collection for HTTP headers with case-insensitive access and proper handling of multi-value headers.
181
182
```python { .api }
183
class Headers:
184
def __init__(self, defaults=None):
185
"""
186
Create a new Headers collection.
187
188
Parameters:
189
- defaults: Initial headers (dict, list of tuples, or Headers)
190
"""
191
192
def get(self, key, default=None, type=None):
193
"""
194
Get header value with optional type conversion.
195
196
Parameters:
197
- key: Header name (case-insensitive)
198
- default: Default value if header not found
199
- type: Callable to convert value
200
201
Returns:
202
Header value, converted by type function if provided
203
"""
204
205
def set(self, _key, _value, **kw):
206
"""
207
Set a header, replacing any existing values.
208
209
Parameters:
210
- _key: Header name
211
- _value: Header value
212
- **kw: Additional parameters for the header
213
"""
214
215
def add(self, _key, _value, **kw):
216
"""
217
Add a header value without replacing existing values.
218
219
Parameters:
220
- _key: Header name
221
- _value: Header value
222
- **kw: Additional parameters for the header
223
"""
224
225
def remove(self, key):
226
"""
227
Remove all values for a header.
228
229
Parameters:
230
- key: Header name to remove
231
"""
232
233
def extend(self, *args, **kwargs):
234
"""
235
Extend headers with new values from dict, list, or other Headers.
236
237
Parameters:
238
- *args: Headers to add (dict, list, Headers)
239
- **kwargs: Header key-value pairs
240
"""
241
242
def to_wsgi_list(self):
243
"""
244
Convert to WSGI-compatible list of (name, value) tuples.
245
246
Returns:
247
List of (header_name, header_value) tuples
248
"""
249
250
def get_all(self, name):
251
"""
252
Get all values for a header as a list.
253
254
Parameters:
255
- name: Header name
256
257
Returns:
258
List of all values for the header
259
"""
260
```
261
262
### EnvironHeaders - WSGI Environment Headers Adapter
263
264
Provides a Headers-like interface to WSGI environ HTTP headers.
265
266
```python { .api }
267
class EnvironHeaders:
268
def __init__(self, environ):
269
"""
270
Create headers adapter for WSGI environ.
271
272
Parameters:
273
- environ: WSGI environment dictionary
274
"""
275
276
def get(self, key, default=None, type=None):
277
"""
278
Get header from WSGI environ.
279
280
Parameters:
281
- key: Header name
282
- default: Default value if not found
283
- type: Type conversion function
284
285
Returns:
286
Header value from environ
287
"""
288
```
289
290
### FileStorage - Uploaded File Wrapper
291
292
Represents an uploaded file with convenient access to metadata and content.
293
294
```python { .api }
295
class FileStorage:
296
def __init__(self, stream=None, filename=None, name=None, content_type=None, content_length=None, headers=None):
297
"""
298
Create a new FileStorage instance.
299
300
Parameters:
301
- stream: File-like object containing the data
302
- filename: Original filename
303
- name: Form field name
304
- content_type: MIME type of the file
305
- content_length: Size of the file in bytes
306
- headers: Additional headers
307
"""
308
309
# Properties
310
filename: str | None # Original filename
311
name: str | None # Form field name
312
content_type: str | None # MIME type
313
content_length: int # File size in bytes
314
headers: Headers # Additional headers
315
stream: IO[bytes] # File data stream
316
317
def save(self, dst, buffer_size=16384):
318
"""
319
Save file to filesystem or file-like object.
320
321
Parameters:
322
- dst: Destination path or file-like object
323
- buffer_size: Buffer size for copying data
324
"""
325
326
def read(self, length=-1):
327
"""
328
Read data from file.
329
330
Parameters:
331
- length: Number of bytes to read (-1 for all)
332
333
Returns:
334
File data as bytes
335
"""
336
337
def readline(self, length=-1):
338
"""
339
Read one line from file.
340
341
Parameters:
342
- length: Maximum number of bytes to read
343
344
Returns:
345
One line as bytes
346
"""
347
348
def readlines(self):
349
"""
350
Read all lines from file.
351
352
Returns:
353
List of lines as bytes
354
"""
355
356
def seek(self, pos, whence=0):
357
"""
358
Seek to position in file.
359
360
Parameters:
361
- pos: Position to seek to
362
- whence: How to interpret pos (0=absolute, 1=relative, 2=from end)
363
"""
364
365
def tell(self):
366
"""
367
Get current position in file.
368
369
Returns:
370
Current byte position
371
"""
372
373
def close(self):
374
"""Close the file stream."""
375
```
376
377
### FileMultiDict - MultiDict for File Uploads
378
379
Specialized MultiDict for handling multiple file uploads with the same field name.
380
381
```python { .api }
382
class FileMultiDict(MultiDict):
383
def __init__(self, mapping=None):
384
"""
385
Create MultiDict specifically for FileStorage objects.
386
387
Parameters:
388
- mapping: Initial file data
389
"""
390
```
391
392
### Accept Headers
393
394
Specialized classes for parsing and handling HTTP Accept headers with quality values.
395
396
```python { .api }
397
class Accept:
398
def __init__(self, values=None):
399
"""
400
Base class for Accept header parsing.
401
402
Parameters:
403
- values: Accept header values with quality parameters
404
"""
405
406
def best_match(self, matches, default=None):
407
"""
408
Find the best match from available options.
409
410
Parameters:
411
- matches: List of available content types/languages/etc.
412
- default: Default value if no match found
413
414
Returns:
415
Best matching value based on quality scores
416
"""
417
418
def quality(self, key):
419
"""
420
Get quality value for a specific option.
421
422
Parameters:
423
- key: Option to get quality for
424
425
Returns:
426
Quality value (0.0 to 1.0)
427
"""
428
429
class MIMEAccept(Accept):
430
def __init__(self, values=None):
431
"""
432
Parse Accept header for MIME types.
433
434
Parameters:
435
- values: Accept header string or parsed values
436
"""
437
438
@property
439
def accept_html(self) -> bool:
440
"""True if HTML is accepted."""
441
442
@property
443
def accept_json(self) -> bool:
444
"""True if JSON is accepted."""
445
446
class LanguageAccept(Accept):
447
def __init__(self, values=None):
448
"""
449
Parse Accept-Language header.
450
451
Parameters:
452
- values: Accept-Language header string or parsed values
453
"""
454
455
class CharsetAccept(Accept):
456
def __init__(self, values=None):
457
"""
458
Parse Accept-Charset header.
459
460
Parameters:
461
- values: Accept-Charset header string or parsed values
462
"""
463
```
464
465
### Authorization and Authentication
466
467
Classes for handling HTTP authentication headers.
468
469
```python { .api }
470
class Authorization:
471
def __init__(self, auth_type, data=None, token=None, username=None, password=None, realm=None, nonce=None, uri=None, nc=None, cnonce=None, response=None, opaque=None, qop=None):
472
"""
473
Parse and represent Authorization header.
474
475
Parameters:
476
- auth_type: Authentication type (Basic, Digest, Bearer, etc.)
477
- data: Raw authorization data
478
- token: Bearer token
479
- username: Username for Basic/Digest auth
480
- password: Password for Basic auth
481
- realm, nonce, uri, nc, cnonce, response, opaque, qop: Digest auth parameters
482
"""
483
484
# Properties
485
type: str # Authentication type
486
username: str | None # Username
487
password: str | None # Password (Basic auth only)
488
realm: str | None # Realm
489
token: str | None # Bearer token
490
491
class WWWAuthenticate:
492
def __init__(self, auth_type=None, values=None, on_update=None):
493
"""
494
Represent WWW-Authenticate header.
495
496
Parameters:
497
- auth_type: Authentication type required
498
- values: Authentication parameters
499
- on_update: Callback for updates
500
"""
501
502
# Properties
503
type: str # Authentication type
504
realm: str | None # Authentication realm
505
```
506
507
### Cache Control
508
509
Classes for parsing and managing Cache-Control headers.
510
511
```python { .api }
512
class RequestCacheControl:
513
def __init__(self, values=None, on_update=None):
514
"""
515
Parse request Cache-Control header.
516
517
Parameters:
518
- values: Cache-Control header values
519
- on_update: Callback for updates
520
"""
521
522
# Properties
523
no_cache: bool # no-cache directive
524
no_store: bool # no-store directive
525
max_age: int | None # max-age in seconds
526
max_stale: int | None # max-stale in seconds
527
min_fresh: int | None # min-fresh in seconds
528
no_transform: bool # no-transform directive
529
only_if_cached: bool # only-if-cached directive
530
531
class ResponseCacheControl:
532
def __init__(self, values=None, on_update=None):
533
"""
534
Parse response Cache-Control header.
535
536
Parameters:
537
- values: Cache-Control header values
538
- on_update: Callback for updates
539
"""
540
541
# Properties
542
no_cache: bool # no-cache directive
543
no_store: bool # no-store directive
544
max_age: int | None # max-age in seconds
545
s_maxage: int | None # s-maxage in seconds
546
public: bool # public directive
547
private: bool # private directive
548
must_revalidate: bool # must-revalidate directive
549
proxy_revalidate: bool # proxy-revalidate directive
550
no_transform: bool # no-transform directive
551
immutable: bool # immutable directive
552
```
553
554
### Content Security Policy
555
556
Class for managing Content Security Policy headers.
557
558
```python { .api }
559
class ContentSecurityPolicy:
560
def __init__(self, values=None, on_update=None):
561
"""
562
Parse and manage CSP header.
563
564
Parameters:
565
- values: CSP directive values
566
- on_update: Callback for updates
567
"""
568
569
# Common directives as properties
570
default_src: list[str] | None
571
script_src: list[str] | None
572
style_src: list[str] | None
573
img_src: list[str] | None
574
connect_src: list[str] | None
575
font_src: list[str] | None
576
object_src: list[str] | None
577
media_src: list[str] | None
578
frame_src: list[str] | None
579
580
def to_header(self):
581
"""
582
Convert to CSP header string.
583
584
Returns:
585
Complete CSP header value
586
"""
587
```
588
589
### Range Headers
590
591
Classes for handling HTTP Range requests and responses.
592
593
```python { .api }
594
class Range:
595
def __init__(self, units, ranges):
596
"""
597
Parse HTTP Range header.
598
599
Parameters:
600
- units: Range units (usually 'bytes')
601
- ranges: List of range specifications
602
"""
603
604
units: str # Range units
605
ranges: list[tuple[int | None, int | None]] # List of (start, end) ranges
606
607
def range_for_length(self, length):
608
"""
609
Get range for content of specific length.
610
611
Parameters:
612
- length: Total content length
613
614
Returns:
615
Tuple of (start, stop) or None if invalid
616
"""
617
618
class ContentRange:
619
def __init__(self, units, start, stop, length=None, on_update=None):
620
"""
621
Represent Content-Range header.
622
623
Parameters:
624
- units: Range units
625
- start: Start position
626
- stop: Stop position
627
- length: Total length
628
- on_update: Callback for updates
629
"""
630
631
units: str # Range units
632
start: int # Start position
633
stop: int # Stop position
634
length: int | None # Total content length
635
```
636
637
### ETags
638
639
Class for handling ETag collections and validation.
640
641
```python { .api }
642
class ETags:
643
def __init__(self, strong_etags=None, weak_etags=None, star_tag=False):
644
"""
645
Represent collection of ETags.
646
647
Parameters:
648
- strong_etags: List of strong ETag values
649
- weak_etags: List of weak ETag values
650
- star_tag: Whether "*" wildcard is present
651
"""
652
653
def contains(self, etag):
654
"""
655
Check if ETag is contained in collection.
656
657
Parameters:
658
- etag: ETag to check
659
660
Returns:
661
True if ETag matches any in collection
662
"""
663
664
def contains_weak(self, etag):
665
"""
666
Check if ETag matches using weak comparison.
667
668
Parameters:
669
- etag: ETag to check
670
671
Returns:
672
True if ETag matches weakly
673
"""
674
675
def to_header(self):
676
"""
677
Convert to header string.
678
679
Returns:
680
ETag header value
681
"""
682
```
683
684
## Usage Examples
685
686
### Working with Form Data
687
688
```python
689
from werkzeug.datastructures import MultiDict
690
from werkzeug.wrappers import Request
691
692
def handle_form(environ, start_response):
693
request = Request(environ)
694
695
# Access form data with type conversion
696
age = request.form.get('age', type=int, default=0)
697
email = request.form.get('email', '')
698
699
# Get all values for checkboxes/multi-select
700
interests = request.form.getlist('interests')
701
702
# Manual MultiDict creation
703
form_data = MultiDict([
704
('name', 'John'),
705
('interests', 'coding'),
706
('interests', 'music')
707
])
708
709
# Add more values
710
form_data.add('interests', 'reading')
711
712
response = Response(f"Age: {age}, Interests: {interests}")
713
return response(environ, start_response)
714
```
715
716
### File Upload Handling
717
718
```python
719
from werkzeug.datastructures import FileStorage
720
from werkzeug.utils import secure_filename
721
import os
722
723
def handle_upload(environ, start_response):
724
request = Request(environ)
725
726
if request.method == 'POST':
727
uploaded_file = request.files.get('document')
728
729
if uploaded_file and uploaded_file.filename:
730
# Secure the filename
731
filename = secure_filename(uploaded_file.filename)
732
733
# Check file type
734
if uploaded_file.content_type.startswith('image/'):
735
# Save to uploads directory
736
upload_path = os.path.join('/uploads', filename)
737
uploaded_file.save(upload_path)
738
739
response = Response(f"Image {filename} uploaded successfully")
740
else:
741
response = Response("Only image files allowed", status=400)
742
else:
743
response = Response("No file provided", status=400)
744
else:
745
response = Response('''
746
<form method="POST" enctype="multipart/form-data">
747
<input type="file" name="document" accept="image/*">
748
<input type="submit" value="Upload">
749
</form>
750
''', mimetype='text/html')
751
752
return response(environ, start_response)
753
```
754
755
### Headers Management
756
757
```python
758
from werkzeug.datastructures import Headers
759
from werkzeug.wrappers import Response
760
761
def api_response():
762
# Create response with custom headers
763
response = Response('{"status": "success"}', mimetype='application/json')
764
765
# Add security headers
766
response.headers['X-Content-Type-Options'] = 'nosniff'
767
response.headers['X-Frame-Options'] = 'DENY'
768
response.headers['X-XSS-Protection'] = '1; mode=block'
769
770
# Add multiple values for same header
771
response.headers.add('Set-Cookie', 'session=abc123; HttpOnly')
772
response.headers.add('Set-Cookie', 'theme=dark; Path=/')
773
774
# Create headers object manually
775
custom_headers = Headers([
776
('Content-Security-Policy', "default-src 'self'"),
777
('Strict-Transport-Security', 'max-age=31536000')
778
])
779
780
# Extend response headers
781
response.headers.extend(custom_headers)
782
783
return response
784
```
785
786
### Content Negotiation with Accept Headers
787
788
```python
789
from werkzeug.wrappers import Request, Response
790
import json
791
792
def content_negotiation(environ, start_response):
793
request = Request(environ)
794
data = {"message": "Hello World", "version": "1.0"}
795
796
# Check client preferences
797
if request.accept_mimetypes.accept_json:
798
response = Response(
799
json.dumps(data),
800
mimetype='application/json'
801
)
802
elif request.accept_mimetypes.accept_html:
803
html = f"<h1>{data['message']}</h1><p>Version: {data['version']}</p>"
804
response = Response(html, mimetype='text/html')
805
elif 'application/xml' in request.accept_mimetypes:
806
xml = f"<root><message>{data['message']}</message><version>{data['version']}</version></root>"
807
response = Response(xml, mimetype='application/xml')
808
else:
809
# Plain text fallback
810
text = f"{data['message']} (v{data['version']})"
811
response = Response(text, mimetype='text/plain')
812
813
return response(environ, start_response)
814
```
815
816
### Range Requests for Large Files
817
818
```python
819
from werkzeug.datastructures import Range
820
from werkzeug.wrappers import Request, Response
821
import os
822
823
def serve_file_with_ranges(environ, start_response):
824
request = Request(environ)
825
file_path = '/path/to/large/file.mp4'
826
file_size = os.path.getsize(file_path)
827
828
# Check for Range header
829
if request.range and request.range.ranges:
830
# Get first range
831
range_spec = request.range.ranges[0]
832
start, end = request.range.range_for_length(file_size)
833
834
if start is not None and end is not None:
835
# Serve partial content
836
with open(file_path, 'rb') as f:
837
f.seek(start)
838
data = f.read(end - start)
839
840
response = Response(
841
data,
842
status=206, # Partial Content
843
mimetype='video/mp4'
844
)
845
response.headers['Content-Range'] = f'bytes {start}-{end-1}/{file_size}'
846
response.headers['Accept-Ranges'] = 'bytes'
847
848
else:
849
# Invalid range
850
response = Response(status=416) # Range Not Satisfiable
851
response.headers['Content-Range'] = f'bytes */{file_size}'
852
else:
853
# Serve full file
854
with open(file_path, 'rb') as f:
855
data = f.read()
856
857
response = Response(data, mimetype='video/mp4')
858
response.headers['Accept-Ranges'] = 'bytes'
859
response.headers['Content-Length'] = str(file_size)
860
861
return response(environ, start_response)
862
```