Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
D
django-rest-framework
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
django-rest-framework
Commits
73cc7755
Commit
73cc7755
authored
Aug 26, 2012
by
Tom Christie
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Drop ImmediateResponse
parent
edd8f596
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
56 additions
and
53 deletions
+56
-53
djangorestframework/exceptions.py
+25
-8
djangorestframework/permissions.py
+6
-12
djangorestframework/response.py
+0
-22
djangorestframework/tests/response.py
+1
-1
djangorestframework/tests/throttling.py
+3
-3
djangorestframework/views.py
+21
-7
No files found.
djangorestframework/exceptions.py
View file @
73cc7755
"""
Handled exceptions raised by REST framework.
In addition Django's built in 403 and 404 exceptions are handled.
(`django.http.Http404` and `django.core.exceptions.PermissionDenied`)
"""
from
djangorestframework
import
status
class
ParseError
(
Exception
):
status_code
=
status
.
HTTP_400_BAD_REQUEST
default_detail
=
'Malformed request'
default_detail
=
'Malformed request
.
'
def
__init__
(
self
,
detail
=
None
):
self
.
detail
=
detail
or
self
.
default_detail
...
...
@@ -11,7 +17,7 @@ class ParseError(Exception):
class
PermissionDenied
(
Exception
):
status_code
=
status
.
HTTP_403_FORBIDDEN
default_detail
=
'You do not have permission to access this resource'
default_detail
=
'You do not have permission to access this resource
.
'
def
__init__
(
self
,
detail
=
None
):
self
.
detail
=
detail
or
self
.
default_detail
...
...
@@ -19,19 +25,30 @@ class PermissionDenied(Exception):
class
MethodNotAllowed
(
Exception
):
status_code
=
status
.
HTTP_405_METHOD_NOT_ALLOWED
default_detail
=
"Method '
%
s' not allowed"
default_detail
=
"Method '
%
s' not allowed
.
"
def
__init__
(
self
,
method
,
detail
):
def
__init__
(
self
,
method
,
detail
=
None
):
self
.
detail
=
(
detail
or
self
.
default_detail
)
%
method
class
UnsupportedMediaType
(
Exception
):
status_code
=
status
.
HTTP_415_UNSUPPORTED_MEDIA_TYPE
default_detail
=
"Unsupported media type '
%
s' in request"
default_detail
=
"Unsupported media type '
%
s' in request
.
"
def
__init__
(
self
,
media_type
,
detail
=
None
):
self
.
detail
=
(
detail
or
self
.
default_detail
)
%
media_type
# class Throttled(Exception):
# def __init__(self, detail):
# self.detail = detail
class
Throttled
(
Exception
):
status_code
=
status
.
HTTP_429_TOO_MANY_REQUESTS
default_detail
=
"Request was throttled. Expected available in
%
d seconds."
def
__init__
(
self
,
wait
,
detail
=
None
):
import
math
self
.
detail
=
(
detail
or
self
.
default_detail
)
%
int
(
math
.
ceil
(
wait
))
REST_FRAMEWORK_EXCEPTIONS
=
(
ParseError
,
PermissionDenied
,
MethodNotAllowed
,
UnsupportedMediaType
,
Throttled
)
djangorestframework/permissions.py
View file @
73cc7755
...
...
@@ -6,9 +6,7 @@ Permission behavior is provided by mixing the :class:`mixins.PermissionsMixin` c
"""
from
django.core.cache
import
cache
from
djangorestframework
import
status
from
djangorestframework.exceptions
import
PermissionDenied
from
djangorestframework.response
import
ImmediateResponse
from
djangorestframework.exceptions
import
PermissionDenied
,
Throttled
import
time
__all__
=
(
...
...
@@ -24,11 +22,6 @@ __all__ = (
SAFE_METHODS
=
[
'GET'
,
'HEAD'
,
'OPTIONS'
]
_503_SERVICE_UNAVAILABLE
=
ImmediateResponse
(
{
'detail'
:
'request was throttled'
},
status
=
status
.
HTTP_503_SERVICE_UNAVAILABLE
)
class
BasePermission
(
object
):
"""
A base class from which all permission classes should inherit.
...
...
@@ -192,7 +185,7 @@ class BaseThrottle(BasePermission):
"""
self
.
history
.
insert
(
0
,
self
.
now
)
cache
.
set
(
self
.
key
,
self
.
history
,
self
.
duration
)
header
=
'status=SUCCESS; next=
%
s
sec'
%
self
.
next
()
header
=
'status=SUCCESS; next=
%
.2
f
sec'
%
self
.
next
()
self
.
view
.
headers
[
'X-Throttle'
]
=
header
def
throttle_failure
(
self
):
...
...
@@ -200,9 +193,10 @@ class BaseThrottle(BasePermission):
Called when a request to the API has failed due to throttling.
Raises a '503 service unavailable' response.
"""
header
=
'status=FAILURE; next=
%
s sec'
%
self
.
next
()
wait
=
self
.
next
()
header
=
'status=FAILURE; next=
%.2
f sec'
%
wait
self
.
view
.
headers
[
'X-Throttle'
]
=
header
raise
_503_SERVICE_UNAVAILABLE
raise
Throttled
(
wait
)
def
next
(
self
):
"""
...
...
@@ -215,7 +209,7 @@ class BaseThrottle(BasePermission):
available_requests
=
self
.
num_requests
-
len
(
self
.
history
)
+
1
return
'
%.2
f'
%
(
remaining_duration
/
float
(
available_requests
)
)
return
remaining_duration
/
float
(
available_requests
)
class
PerUserThrottling
(
BaseThrottle
):
...
...
djangorestframework/response.py
View file @
73cc7755
...
...
@@ -161,25 +161,3 @@ class Response(SimpleTemplateResponse):
},
status
=
status
.
HTTP_406_NOT_ACCEPTABLE
,
view
=
self
.
view
,
request
=
self
.
request
,
renderers
=
[
renderer
])
class
ImmediateResponse
(
Response
,
Exception
):
"""
An exception representing an Response that should be returned immediately.
Any content should be serialized as-is, without being filtered.
"""
#TODO: this is just a temporary fix, the whole rendering/support for ImmediateResponse, should be remade : see issue #163
def
render
(
self
):
try
:
return
super
(
Response
,
self
)
.
render
()
except
ImmediateResponse
:
renderer
,
media_type
=
self
.
_determine_renderer
()
self
.
renderers
.
remove
(
renderer
)
if
len
(
self
.
renderers
)
==
0
:
raise
RuntimeError
(
'Caught an ImmediateResponse while '
\
'trying to render an ImmediateResponse'
)
return
self
.
render
()
def
__init__
(
self
,
*
args
,
**
kwargs
):
self
.
response
=
Response
(
*
args
,
**
kwargs
)
djangorestframework/tests/response.py
View file @
73cc7755
...
...
@@ -4,7 +4,7 @@ import unittest
from
django.conf.urls.defaults
import
patterns
,
url
,
include
from
django.test
import
TestCase
from
djangorestframework.response
import
Response
,
NotAcceptable
,
ImmediateResponse
from
djangorestframework.response
import
Response
,
NotAcceptable
from
djangorestframework.views
import
View
from
djangorestframework.compat
import
RequestFactory
from
djangorestframework
import
status
...
...
djangorestframework/tests/throttling.py
View file @
73cc7755
...
...
@@ -45,7 +45,7 @@ class ThrottlingTests(TestCase):
request
=
self
.
factory
.
get
(
'/'
)
for
dummy
in
range
(
4
):
response
=
MockView
.
as_view
()(
request
)
self
.
assertEqual
(
503
,
response
.
status_code
)
self
.
assertEqual
(
429
,
response
.
status_code
)
def
set_throttle_timer
(
self
,
view
,
value
):
"""
...
...
@@ -62,7 +62,7 @@ class ThrottlingTests(TestCase):
request
=
self
.
factory
.
get
(
'/'
)
for
dummy
in
range
(
4
):
response
=
MockView
.
as_view
()(
request
)
self
.
assertEqual
(
503
,
response
.
status_code
)
self
.
assertEqual
(
429
,
response
.
status_code
)
# Advance the timer by one second
self
.
set_throttle_timer
(
MockView
,
1
)
...
...
@@ -90,7 +90,7 @@ class ThrottlingTests(TestCase):
"""
Ensure request rate is limited globally per View for PerViewThrottles
"""
self
.
ensure_is_throttled
(
MockView_PerViewThrottling
,
503
)
self
.
ensure_is_throttled
(
MockView_PerViewThrottling
,
429
)
def
ensure_response_header_contains_proper_throttle_field
(
self
,
view
,
expected_headers
):
"""
...
...
djangorestframework/views.py
View file @
73cc7755
...
...
@@ -6,12 +6,14 @@ By setting or modifying class attributes on your view, you change it's predefine
"""
import
re
from
django.core.exceptions
import
PermissionDenied
from
django.http
import
Http404
from
django.utils.html
import
escape
from
django.utils.safestring
import
mark_safe
from
django.views.decorators.csrf
import
csrf_exempt
from
djangorestframework.compat
import
View
as
DjangoView
,
apply_markdown
from
djangorestframework.response
import
Response
,
ImmediateResponse
from
djangorestframework.response
import
Response
from
djangorestframework.request
import
Request
from
djangorestframework
import
renderers
,
parsers
,
authentication
,
permissions
,
status
,
exceptions
...
...
@@ -219,13 +221,27 @@ class View(DjangoView):
response
[
key
]
=
value
return
response
def
handle_exception
(
self
,
exc
):
"""
Handle any exception that occurs, by returning an appropriate response,
or re-raising the error.
"""
if
isinstance
(
exc
,
exceptions
.
REST_FRAMEWORK_EXCEPTIONS
):
return
Response
({
'detail'
:
exc
.
detail
},
status
=
exc
.
status_code
)
elif
isinstance
(
exc
,
Http404
):
return
Response
({
'detail'
:
'Not found'
},
status
=
status
.
HTTP_404_NOT_FOUND
)
elif
isinstance
(
exc
,
PermissionDenied
):
return
Response
({
'detail'
:
'Permission denied'
},
status
=
status
.
HTTP_403_FORBIDDEN
)
raise
# Note: session based authentication is explicitly CSRF validated,
# all other authentication is CSRF exempt.
@csrf_exempt
def
dispatch
(
self
,
request
,
*
args
,
**
kwargs
):
request
=
Request
(
request
,
parsers
=
self
.
parsers
,
authentication
=
self
.
authentication
)
self
.
request
=
request
self
.
args
=
args
self
.
kwargs
=
kwargs
self
.
headers
=
self
.
default_response_headers
...
...
@@ -244,10 +260,8 @@ class View(DjangoView):
response
=
handler
(
request
,
*
args
,
**
kwargs
)
except
ImmediateResponse
,
exc
:
response
=
exc
.
response
except
(
exceptions
.
ParseError
,
exceptions
.
PermissionDenied
)
as
exc
:
response
=
Response
({
'detail'
:
exc
.
detail
},
status
=
exc
.
status_code
)
except
Exception
as
exc
:
response
=
self
.
handle_exception
(
exc
)
self
.
response
=
self
.
final
(
request
,
response
,
*
args
,
**
kwargs
)
return
self
.
response
...
...
@@ -265,4 +279,4 @@ class View(DjangoView):
for
name
,
field
in
form
.
fields
.
iteritems
():
field_name_types
[
name
]
=
field
.
__class__
.
__name__
content
[
'fields'
]
=
field_name_types
raise
Immediate
Response
(
content
,
status
=
status
.
HTTP_200_OK
)
raise
Response
(
content
,
status
=
status
.
HTTP_200_OK
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment