Skip to content

Commit aaeda57

Browse files
committed
feat: handles put and patch body
1 parent 95dca76 commit aaeda57

File tree

1 file changed

+60
-7
lines changed

1 file changed

+60
-7
lines changed

django_sonar/middlewares/requests.py

Lines changed: 60 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,6 @@ def __init__(self, get_response):
2020
tracemalloc.start() # Start tracing memory allocation
2121

2222
def __call__(self, request):
23-
# Get the list of excluded paths from settings
24-
excluded_paths = settings.DJANGO_SONAR.get('excludes', [])
25-
2623
# Check if the request path is excluded
2724
if self._should_exclude(request.path):
2825
return self.get_response(request)
@@ -47,7 +44,7 @@ def __call__(self, request):
4744

4845
# Capture GET/POST data
4946
get_payload = request.GET.dict()
50-
post_payload = self.get_post_payload(request)
47+
post_payload = self.get_body_payload(request)
5148

5249
# Process the request
5350
response = self.get_response(request)
@@ -152,9 +149,65 @@ def get_client_ip(self, request):
152149
def is_ajax(self, request):
153150
return request.headers.get('X-Requested-With') == 'XMLHttpRequest'
154151

155-
def get_post_payload(self, request):
156-
post_data = request.POST.copy()
157-
return post_data
152+
def get_body_payload(self, request):
153+
"""Get request body data for all HTTP methods (POST, PUT, PATCH, etc.)"""
154+
if request.method == 'GET':
155+
return {}
156+
157+
# For POST requests, use the standard request.POST
158+
if request.method == 'POST':
159+
return request.POST.copy()
160+
161+
# For PUT, PATCH, and other methods, parse the request body
162+
try:
163+
content_type = request.content_type.lower()
164+
165+
# Handle JSON content
166+
if 'application/json' in content_type:
167+
if hasattr(request, 'body') and request.body:
168+
return json.loads(request.body.decode('utf-8'))
169+
return {}
170+
171+
# Handle form-encoded content
172+
elif 'application/x-www-form-urlencoded' in content_type:
173+
if hasattr(request, 'body') and request.body:
174+
parsed_data = parse_qs(request.body.decode('utf-8'))
175+
# Convert lists to single values for consistency with request.POST
176+
return {k: v[0] if len(v) == 1 else v for k, v in parsed_data.items()}
177+
return {}
178+
179+
# Handle multipart form data (files)
180+
elif 'multipart/form-data' in content_type:
181+
# For multipart, try to get data from request.FILES and any parsed data
182+
data = {}
183+
if hasattr(request, 'FILES') and request.FILES:
184+
data['_files'] = list(request.FILES.keys())
185+
# Note: Django doesn't populate request.POST for PUT/PATCH multipart,
186+
# so we'd need custom parsing for complex multipart PUT/PATCH requests
187+
return data
188+
189+
# For other content types, store raw body (truncated for safety)
190+
else:
191+
if hasattr(request, 'body') and request.body:
192+
body_str = request.body.decode('utf-8', errors='ignore')
193+
# Truncate large bodies to avoid storage issues
194+
if len(body_str) > 10000:
195+
body_str = body_str[:10000] + '... (truncated)'
196+
return {'_raw_body': body_str, '_content_type': content_type}
197+
return {}
198+
199+
except (json.JSONDecodeError, UnicodeDecodeError, Exception) as e:
200+
# If parsing fails, store error info and raw body (truncated)
201+
try:
202+
body_str = request.body.decode('utf-8', errors='ignore')[:1000]
203+
except:
204+
body_str = '<unable to decode>'
205+
206+
return {
207+
'_parse_error': str(e),
208+
'_raw_body': body_str,
209+
'_content_type': getattr(request, 'content_type', 'unknown')
210+
}
158211

159212
def process_exception(self, request, exception):
160213
"""Process the exception and keep it in local storage."""

0 commit comments

Comments
 (0)