Skip to content

Commit 9c9ffb1

Browse files
authored
made Browsable API base template cachable: omit CSRF token when unnecessary (encode#7717)
HTML responses generated by the Browsable API otherwise generate inconsistent ETAGs -- due to the presence of CSRF tokens in the response -- even when the API is read-only, (and as such when the response contains no resource-modifying forms, i.e. neither POST nor PUT forms, which might require the CSRF token). While the template was appropriately including CSRF tokens only within POST and PUT forms, its AJAX overlay included the CSRF token in *every* response, regardless of whether it would be needed. This change brings the logic of the `script` block into line with that of the rest of the template -- and such that read-only APIs (and really the Browsable API pages of *any* read-only resources) will not needlessly include the CSRF token, and will now be safely cachable -- by both back-end systems and by the user agent.
1 parent b0ca248 commit 9c9ffb1

File tree

2 files changed

+15
-7
lines changed

2 files changed

+15
-7
lines changed

rest_framework/templates/rest_framework/base.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ <h1>{{ name }}</h1>
290290
<script>
291291
window.drf = {
292292
csrfHeaderName: "{{ csrf_header_name|default:'X-CSRFToken' }}",
293-
csrfToken: "{% if request %}{{ csrf_token }}{% endif %}"
293+
csrfToken: "{% if request %}{% if post_form or put_form %}{{ csrf_token }}{% endif %}{% endif %}"
294294
};
295295
</script>
296296
<script src="{% static "rest_framework/js/jquery-3.5.1.min.js" %}"></script>

tests/test_templates.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,23 @@
33
from django.shortcuts import render
44

55

6-
def test_base_template_with_context():
7-
context = {'request': True, 'csrf_token': 'TOKEN'}
8-
result = render({}, 'rest_framework/base.html', context=context)
9-
assert re.search(r'\bcsrfToken: "TOKEN"', result.content.decode())
10-
11-
126
def test_base_template_with_no_context():
137
# base.html should be renderable with no context,
148
# so it can be easily extended.
159
result = render({}, 'rest_framework/base.html')
1610
# note that this response will not include a valid CSRF token
1711
assert re.search(r'\bcsrfToken: ""', result.content.decode())
12+
13+
14+
def test_base_template_with_simple_context():
15+
context = {'request': True, 'csrf_token': 'TOKEN'}
16+
result = render({}, 'rest_framework/base.html', context=context)
17+
# note that response will STILL not include a CSRF token
18+
assert re.search(r'\bcsrfToken: ""', result.content.decode())
19+
20+
21+
def test_base_template_with_editing_context():
22+
context = {'request': True, 'post_form': object(), 'csrf_token': 'TOKEN'}
23+
result = render({}, 'rest_framework/base.html', context=context)
24+
# response includes a CSRF token in support of the POST form
25+
assert re.search(r'\bcsrfToken: "TOKEN"', result.content.decode())

0 commit comments

Comments
 (0)