Coverage for /Users/davegaeddert/Developer/dropseed/plain/plain/plain/internal/middleware/slash.py: 57%
28 statements
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-23 11:16 -0600
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-23 11:16 -0600
1from plain.http import ResponsePermanentRedirect
2from plain.runtime import settings
3from plain.urls import is_valid_path
4from plain.utils.http import escape_leading_slashes
7class RedirectSlashMiddleware:
8 def __init__(self, get_response):
9 self.get_response = get_response
11 def __call__(self, request):
12 """
13 Rewrite the URL based on settings.APPEND_SLASH
14 """
16 response = self.get_response(request)
18 """
19 When the status code of the response is 404, it may redirect to a path
20 with an appended slash if should_redirect_with_slash() returns True.
21 """
22 # If the given URL is "Not Found", then check if we should redirect to
23 # a path with a slash appended.
24 if response.status_code == 404 and self.should_redirect_with_slash(request):
25 return ResponsePermanentRedirect(self.get_full_path_with_slash(request))
27 return response
29 def should_redirect_with_slash(self, request):
30 """
31 Return True if settings.APPEND_SLASH is True and appending a slash to
32 the request path turns an invalid path into a valid one.
33 """
34 if settings.APPEND_SLASH and not request.path_info.endswith("/"):
35 urlconf = getattr(request, "urlconf", None)
36 if not is_valid_path(request.path_info, urlconf):
37 match = is_valid_path(f"{request.path_info}/", urlconf)
38 if match:
39 view = match.func
40 return getattr(view, "should_append_slash", True)
41 return False
43 def get_full_path_with_slash(self, request):
44 """
45 Return the full path of the request with a trailing slash appended.
47 Raise a RuntimeError if settings.DEBUG is True and request.method is
48 POST, PUT, or PATCH.
49 """
50 new_path = request.get_full_path(force_append_slash=True)
51 # Prevent construction of scheme relative urls.
52 new_path = escape_leading_slashes(new_path)
53 if settings.DEBUG and request.method in ("POST", "PUT", "PATCH"):
54 raise RuntimeError(
55 f"You called this URL via {request.method}, but the URL doesn't end "
56 "in a slash and you have APPEND_SLASH set. Plain can't "
57 f"redirect to the slash URL while maintaining {request.method} data. "
58 f"Change your form to point to {request.get_host() + new_path} (note the trailing "
59 "slash), or set APPEND_SLASH=False in your Plain settings."
60 )
61 return new_path