Skip to content
This repository was archived by the owner on Oct 23, 2023. It is now read-only.

Commit 5193458

Browse files
eprikazcashwoods
authored andcommitted
Fix unavailable request data while using with REST framework #591 (#1061)
* Add DjangoRestFrameworkCompatMiddleware * Update DjangoRestFrameworkCompatMiddleware to not require get_response in constructor This is for compatibility with Django 1.10 and earlier * Add unit tests for DjangoRestFrameworkCompatMiddleware * Update docstring in install_middleware function * Convert DjangoRestFrameworkCompatMiddleware to be backwards-compatible * Make sure that middlewares are not set in test_request_data_unavailable_if_request_is_read
1 parent 3b88fd6 commit 5193458

File tree

5 files changed

+67
-10
lines changed

5 files changed

+67
-10
lines changed

raven/contrib/django/middleware/__init__.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,3 +118,18 @@ def request_finished(self, **kwargs):
118118

119119

120120
SentryLogMiddleware = SentryMiddleware
121+
122+
123+
class DjangoRestFrameworkCompatMiddleware(MiddlewareMixin):
124+
def process_request(self, request):
125+
"""
126+
Access request.body, otherwise it might not be accessible later
127+
after request has been read/streamed
128+
"""
129+
content_type = request.META.get('CONTENT_TYPE', '')
130+
if 'application/x-www-form-urlencoded' in content_type:
131+
pass
132+
elif 'multipart/form-data' in content_type:
133+
pass
134+
else:
135+
request.body # forces stream to be read into memory

raven/contrib/django/models.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -212,26 +212,23 @@ def register_serializers():
212212
import raven.contrib.django.serializers # NOQA
213213

214214

215-
def install_middleware():
215+
def install_middleware(middleware_name, lookup_names=None):
216216
"""
217-
Force installation of SentryMiddlware if it's not explicitly present.
218-
219-
This ensures things like request context and transaction names are made
220-
available.
217+
Install specified middleware
221218
"""
222-
name = 'raven.contrib.django.middleware.SentryMiddleware'
223-
all_names = (name, 'raven.contrib.django.middleware.SentryLogMiddleware')
219+
if lookup_names is None:
220+
lookup_names = (middleware_name,)
224221
# default settings.MIDDLEWARE is None
225222
middleware_attr = 'MIDDLEWARE' if getattr(settings,
226223
'MIDDLEWARE',
227224
None) is not None \
228225
else 'MIDDLEWARE_CLASSES'
229226
# make sure to get an empty tuple when attr is None
230227
middleware = getattr(settings, middleware_attr, ()) or ()
231-
if set(all_names).isdisjoint(set(middleware)):
228+
if set(lookup_names).isdisjoint(set(middleware)):
232229
setattr(settings,
233230
middleware_attr,
234-
type(middleware)((name,)) + middleware)
231+
type(middleware)((middleware_name,)) + middleware)
235232

236233

237234
_setup_lock = Lock()
@@ -250,7 +247,13 @@ def initialize():
250247

251248
try:
252249
register_serializers()
253-
install_middleware()
250+
install_middleware(
251+
'raven.contrib.django.middleware.SentryMiddleware',
252+
(
253+
'raven.contrib.django.middleware.SentryMiddleware',
254+
'raven.contrib.django.middleware.SentryLogMiddleware'))
255+
install_middleware(
256+
'raven.contrib.django.middleware.DjangoRestFrameworkCompatMiddleware')
254257

255258
# XXX(dcramer): maybe this setting should disable ALL of this?
256259
if not getattr(settings, 'DISABLE_SENTRY_INSTRUMENTATION', False):

tests/contrib/django/tests.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@
4141
from raven.transport import HTTPTransport
4242
from raven.utils.serializer import transform
4343

44+
from .views import AppError
45+
4446
#from .models import MyTestModel
4547

4648
DJANGO_15 = django.VERSION >= (1, 5, 0)
@@ -182,6 +184,33 @@ def test_view_exception(self):
182184
assert 'request' in event
183185
assert event['request']['url'] == 'http://testserver{}'.format(path)
184186

187+
def test_request_data_unavailable_if_request_is_read(self):
188+
with Settings(**{MIDDLEWARE_ATTR: []}):
189+
path = reverse('sentry-readrequest-raise-exc')
190+
self.assertRaises(
191+
AppError,
192+
self.client.post,
193+
path,
194+
'{"a":"b"}',
195+
content_type='application/json')
196+
assert len(self.raven.events) == 1
197+
event = self.raven.events.pop(0)
198+
assert event['request']['data'] == '<unavailable>'
199+
200+
def test_djangorestframeworkcompatmiddleware_fills_request_data(self):
201+
with Settings(**{MIDDLEWARE_ATTR: [
202+
'raven.contrib.django.middleware.DjangoRestFrameworkCompatMiddleware']}):
203+
path = reverse('sentry-readrequest-raise-exc')
204+
self.assertRaises(
205+
AppError,
206+
self.client.post,
207+
path,
208+
'{"a":"b"}',
209+
content_type='application/json')
210+
assert len(self.raven.events) == 1
211+
event = self.raven.events.pop(0)
212+
assert event['request']['data'] == b'{"a":"b"}'
213+
185214
def test_capture_event_with_request_middleware(self):
186215
path = reverse('sentry-trigger-event')
187216
resp = self.client.get(path)

tests/contrib/django/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ def handler500(request, exception=None):
2626
url(r'^no-error$', views.no_error, name='sentry-no-error'),
2727
url(r'^fake-login$', views.fake_login, name='sentry-fake-login'),
2828
url(r'^trigger-500$', views.raise_exc, name='sentry-raise-exc'),
29+
url(r'^trigger-500-readrequest$', views.read_request_and_raise_exc, name='sentry-readrequest-raise-exc'),
2930
url(r'^trigger-500-ioerror$', views.raise_ioerror, name='sentry-raise-ioerror'),
3031
url(r'^trigger-500-decorated$', views.decorated_raise_exc, name='sentry-raise-exc-decor'),
3132
url(r'^trigger-500-django$', views.django_exc, name='sentry-django-exc'),

tests/contrib/django/views.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
import logging
88

99

10+
class AppError(Exception):
11+
pass
12+
13+
1014
def no_error(request):
1115
return HttpResponse('')
1216

@@ -23,6 +27,11 @@ def raise_exc(request):
2327
raise Exception(request.GET.get('message', 'view exception'))
2428

2529

30+
def read_request_and_raise_exc(request):
31+
request.read()
32+
raise AppError()
33+
34+
2635
def raise_ioerror(request):
2736
raise IOError(request.GET.get('message', 'view exception'))
2837

0 commit comments

Comments
 (0)