Error reporting¶
When you’re running a public site you should always turn off the
DEBUG setting. That will make your server run much faster, and will
also prevent malicious users from seeing details of your application that can be
revealed by the error pages.
However, running with DEBUG set to False means you’ll never see
errors generated by your site – everyone will instead see your public error
pages. You need to keep track of errors that occur in deployed sites, so Django
can be configured to create reports with details about those errors.
Email reports¶
Server errors¶
When DEBUG is False, Django will email the users listed in the
ADMINS setting whenever your code raises an unhandled exception and
results in an internal server error (strictly speaking, for any response with
an HTTP status code of 500 or greater). This gives the administrators immediate
notification of any errors. The ADMINS will get a description of the
error, a complete Python traceback, and details about the HTTP request that
caused the error.
Note
In order to send email, Django requires a few settings telling it
how to connect to your mail server. At the very least, you’ll need
to specify EMAIL_HOST and possibly
EMAIL_HOST_USER and EMAIL_HOST_PASSWORD,
though other settings may be also required depending on your mail
server’s configuration. Consult the Django settings
documentation for a full list of email-related
settings.
By default, Django will send email from root@localhost. However, some mail
providers reject all email from this address. To use a different sender
address, modify the SERVER_EMAIL setting.
To activate this behavior, put the email addresses of the recipients in the
ADMINS setting.
404 errors¶
Django can also be configured to email errors about broken links (404 “page
not found” errors). Django sends emails about 404 errors when:
DEBUGisFalse;- Your
MIDDLEWAREsetting includes
django.middleware.common.BrokenLinkEmailsMiddleware.
If those conditions are met, Django will email the users listed in the
MANAGERS setting whenever your code raises a 404 and the request has
a referer. It doesn’t bother to email for 404s that don’t have a referer –
those are usually people typing in broken URLs or broken Web bots. It also
ignores 404s when the referer is equal to the requested URL, since this
behavior is from broken Web bots too.
You can tell Django to stop reporting particular 404s by tweaking the
IGNORABLE_404_URLS setting. It should be a list of compiled
regular expression objects. For example:
import re IGNORABLE_404_URLS = [ re.compile(r'.(php|cgi)$'), re.compile(r'^/phpmyadmin/'), ]
In this example, a 404 to any URL ending with .php or .cgi will not be
reported. Neither will any URL starting with /phpmyadmin/.
The following example shows how to exclude some conventional URLs that browsers and
crawlers often request:
import re IGNORABLE_404_URLS = [ re.compile(r'^/apple-touch-icon.*.png$'), re.compile(r'^/favicon.ico$'), re.compile(r'^/robots.txt$'), ]
(Note that these are regular expressions, so we put a backslash in front of
periods to escape them.)
If you’d like to customize the behavior of
django.middleware.common.BrokenLinkEmailsMiddleware further (for
example to ignore requests coming from web crawlers), you should subclass it
and override its methods.
See also
404 errors are logged using the logging framework. By default, these log
records are ignored, but you can use them for error reporting by writing a
handler and configuring logging appropriately.
Filtering error reports¶
Warning
Filtering sensitive data is a hard problem, and it’s nearly impossible to
guarantee that sensitive data won’t leak into an error report. Therefore,
error reports should only be available to trusted team members and you
should avoid transmitting error reports unencrypted over the Internet
(such as through email).
Filtering sensitive information¶
Error reports are really helpful for debugging errors, so it is generally
useful to record as much relevant information about those errors as possible.
For example, by default Django records the full traceback for the
exception raised, each traceback frame’s local variables, and the
HttpRequest’s attributes.
However, sometimes certain types of information may be too sensitive and thus
may not be appropriate to be kept track of, for example a user’s password or
credit card number. So in addition to filtering out settings that appear to be
sensitive as described in the DEBUG documentation, Django offers a
set of function decorators to help you control which information should be
filtered out of error reports in a production environment (that is, where
DEBUG is set to False): sensitive_variables() and
sensitive_post_parameters().
-
sensitive_variables(*variables)¶ -
If a function (either a view or any regular callback) in your code uses
local variables susceptible to contain sensitive information, you may
prevent the values of those variables from being included in error reports
using thesensitive_variablesdecorator:from django.views.decorators.debug import sensitive_variables @sensitive_variables('user', 'pw', 'cc') def process_info(user): pw = user.pass_word cc = user.credit_card_number name = user.name ...
In the above example, the values for the
user,pwandcc
variables will be hidden and replaced with stars (**********) in the
error reports, whereas the value of thenamevariable will be
disclosed.To systematically hide all local variables of a function from error logs,
do not provide any argument to thesensitive_variablesdecorator:@sensitive_variables() def my_function(): ...
When using multiple decorators
If the variable you want to hide is also a function argument (e.g.
‘user’ in the following example), and if the decorated function has
multiple decorators, then make sure to place@sensitive_variables
at the top of the decorator chain. This way it will also hide the
function argument as it gets passed through the other decorators:@sensitive_variables('user', 'pw', 'cc') @some_decorator @another_decorator def process_info(user): ...
-
sensitive_post_parameters(*parameters)¶ -
If one of your views receives an
HttpRequestobject
withPOST parameterssusceptible to
contain sensitive information, you may prevent the values of those
parameters from being included in the error reports using the
sensitive_post_parametersdecorator:from django.views.decorators.debug import sensitive_post_parameters @sensitive_post_parameters('pass_word', 'credit_card_number') def record_user_profile(request): UserProfile.create( user=request.user, password=request.POST['pass_word'], credit_card=request.POST['credit_card_number'], name=request.POST['name'], ) ...
In the above example, the values for the
pass_wordand
credit_card_numberPOST parameters will be hidden and replaced with
stars (**********) in the request’s representation inside the error
reports, whereas the value of thenameparameter will be disclosed.To systematically hide all POST parameters of a request in error reports,
do not provide any argument to thesensitive_post_parametersdecorator:@sensitive_post_parameters() def my_view(request): ...
All POST parameters are systematically filtered out of error reports for
certaindjango.contrib.auth.viewsviews (login,
password_reset_confirm,password_change, andadd_viewand
user_change_passwordin theauthadmin) to prevent the leaking of
sensitive information such as user passwords.
Custom error reports¶
All sensitive_variables() and sensitive_post_parameters() do is,
respectively, annotate the decorated function with the names of sensitive
variables and annotate the HttpRequest object with the names of sensitive
POST parameters, so that this sensitive information can later be filtered out
of reports when an error occurs. The actual filtering is done by Django’s
default error reporter filter:
django.views.debug.SafeExceptionReporterFilter. This filter uses the
decorators’ annotations to replace the corresponding values with stars
(**********) when the error reports are produced. If you wish to override or
customize this default behavior for your entire site, you need to define your
own filter class and tell Django to use it via the
DEFAULT_EXCEPTION_REPORTER_FILTER setting:
DEFAULT_EXCEPTION_REPORTER_FILTER = 'path.to.your.CustomExceptionReporterFilter'
You may also control in a more granular way which filter to use within any
given view by setting the HttpRequest’s exception_reporter_filter
attribute:
def my_view(request): if request.user.is_authenticated: request.exception_reporter_filter = CustomExceptionReporterFilter() ...
Your custom filter class needs to inherit from
django.views.debug.SafeExceptionReporterFilter and may override the
following methods:
-
class
SafeExceptionReporterFilter¶
-
SafeExceptionReporterFilter.is_active(request)¶ -
Returns
Trueto activate the filtering operated in the other methods.
By default the filter is active ifDEBUGisFalse.
-
SafeExceptionReporterFilter.get_post_parameters(request)¶ -
Returns the filtered dictionary of POST parameters. By default it replaces
the values of sensitive parameters with stars (**********).
-
SafeExceptionReporterFilter.get_traceback_frame_variables(request, tb_frame)¶ -
Returns the filtered dictionary of local variables for the given traceback
frame. By default it replaces the values of sensitive variables with stars
(**********).
See also
You can also set up custom error reporting by writing a custom piece of
exception middleware. If you do write custom
error handling, it’s a good idea to emulate Django’s built-in error handling
and only report/log errors if DEBUG is False.
Исключения¶
Исключения… позволяют чисто организовать обработку ошибок в центральном или высокоуровневом месте в структуре программы.
‒ Doug Hellmann, Python Exception Handling Techniques
Представления фреймворка REST обрабатывают различные исключения и возвращают соответствующие ответы на ошибки.
Обрабатываемыми исключениями являются:
-
Подклассы
APIException, поднятые внутри фреймворка REST. -
Исключение Django
Http404. -
Исключение Django
PermissionDenied.
В каждом случае фреймворк REST возвращает ответ с соответствующим кодом состояния и типом содержимого. В теле ответа будут содержаться любые дополнительные сведения о характере ошибки.
Большинство ответов на ошибки будут содержать ключ detail в теле ответа.
Например, следующий запрос:
DELETE http://api.example.com/foo/bar HTTP/1.1 Accept: application/json
Может быть получен ответ об ошибке, указывающий на то, что метод DELETE не разрешен на данном ресурсе:
HTTP/1.1 405 Method Not Allowed Content-Type: application/json Content-Length: 42 {"detail": "Method 'DELETE' not allowed."}
Ошибки валидации обрабатываются несколько иначе, и в качестве ключей в ответе будут указаны имена полей. Если ошибка валидации не относится к конкретному полю, то будет использован ключ «non_field_errors» или любое строковое значение, установленное для параметра NON_FIELD_ERRORS_KEY.
Пример ошибки валидации может выглядеть следующим образом:
HTTP/1.1 400 Bad Request Content-Type: application/json Content-Length: 94 {"amount": ["A valid integer is required."], "description": ["This field may not be blank."]}
Пользовательская обработка исключений¶
Вы можете реализовать пользовательскую обработку исключений, создав функцию-обработчик, которая преобразует исключения, возникающие в представлениях вашего API, в объекты ответа. Это позволит вам контролировать стиль ответов на ошибки, используемый вашим API.
Функция должна принимать пару аргументов, первый из которых — обрабатываемое исключение, а второй — словарь, содержащий любой дополнительный контекст, например, обрабатываемое в данный момент представление. Функция обработчика исключения должна либо вернуть объект Response, либо вернуть None, если исключение не может быть обработано. Если обработчик возвращает None, то исключение будет повторно поднято и Django вернет стандартный ответ HTTP 500 „server error“.
Например, вы можете захотеть убедиться, что все ответы на ошибки включают код состояния HTTP в теле ответа, например, так:
HTTP/1.1 405 Method Not Allowed Content-Type: application/json Content-Length: 62 {"status_code": 405, "detail": "Method 'DELETE' not allowed."}
Чтобы изменить стиль ответа, вы можете написать следующий пользовательский обработчик исключений:
from rest_framework.views import exception_handler def custom_exception_handler(exc, context): # Call REST framework's default exception handler first, # to get the standard error response. response = exception_handler(exc, context) # Now add the HTTP status code to the response. if response is not None: response.data['status_code'] = response.status_code return response
Аргумент context не используется обработчиком по умолчанию, но может быть полезен, если обработчику исключения нужна дополнительная информация, например, обрабатываемое в данный момент представление, доступ к которому можно получить в виде context['view'].
Обработчик исключений также должен быть настроен в ваших настройках, используя клавишу настройки EXCEPTION_HANDLER. Например:
REST_FRAMEWORK = { 'EXCEPTION_HANDLER': 'my_project.my_app.utils.custom_exception_handler' }
Если параметр 'EXCEPTION_HANDLER' не указан, то по умолчанию используется стандартный обработчик исключений, предоставляемый фреймворком REST:
REST_FRAMEWORK = { 'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler' }
Обратите внимание, что обработчик исключений будет вызываться только для ответов, сгенерированных поднятыми исключениями. Он не будет использоваться для любых ответов, возвращаемых непосредственно представлением, например, ответов HTTP_400_BAD_REQUEST, которые возвращаются общими представлениями при неудачной проверке сериализатора.
Справочник по API¶
APIException¶
Подпись: APIException()
базовый класс для всех исключений, возникающих внутри класса APIView или @api_view.
Чтобы обеспечить пользовательское исключение, подкласс APIException и установите атрибуты .status_code , .default_detail , и default_code на класс.
Например, если ваш API полагается на сторонний сервис, который иногда может быть недоступен, вы можете захотеть реализовать исключение для кода ответа HTTP «503 Service Unavailable». Это можно сделать следующим образом:
from rest_framework.exceptions import APIException class ServiceUnavailable(APIException): status_code = 503 default_detail = 'Service temporarily unavailable, try again later.' default_code = 'service_unavailable'
Проверка исключений API¶
Существует ряд различных свойств, доступных для проверки состояния исключения API. Вы можете использовать их для создания пользовательской обработки исключений в вашем проекте.
Доступными атрибутами и методами являются:
-
.detail— Возвращает текстовое описание ошибки. -
.get_codes()— Возвращает идентификатор кода ошибки. -
.get_full_details()— Возвращает как текстовое описание, так и идентификатор кода.
В большинстве случаев деталь ошибки будет простым элементом:
>>> print(exc.detail) You do not have permission to perform this action. >>> print(exc.get_codes()) permission_denied >>> print(exc.get_full_details()) {'message':'You do not have permission to perform this action.','code':'permission_denied'}
В случае ошибок валидации деталь ошибки будет представлять собой список или словарь элементов:
>>> print(exc.detail) {"name":"This field is required.","age":"A valid integer is required."} >>> print(exc.get_codes()) {"name":"required","age":"invalid"} >>> print(exc.get_full_details()) {"name":{"message":"This field is required.","code":"required"},"age":{"message":"A valid integer is required.","code":"invalid"}}
ParseError¶
Подпись: ParseError(detail=None, code=None)
Возникает, если запрос содержит неправильно сформированные данные при доступе к request.data.
По умолчанию это исключение приводит к ответу с кодом состояния HTTP «400 Bad Request».
AuthenticationFailed¶
Подпись: AuthenticationFailed(detail=None, code=None)
Возникает, когда входящий запрос содержит неправильную аутентификацию.
По умолчанию это исключение приводит к ответу с кодом состояния HTTP «401 Unauthenticated», но оно также может привести к ответу «403 Forbidden», в зависимости от используемой схемы аутентификации. Более подробную информацию см. в authentication documentation.
NotAuthenticated¶
Подпись: NotAuthenticated(detail=None, code=None)
Возникает, когда неаутентифицированный запрос не прошел проверку на разрешение.
По умолчанию это исключение приводит к ответу с кодом состояния HTTP «401 Unauthenticated», но оно также может привести к ответу «403 Forbidden», в зависимости от используемой схемы аутентификации. Более подробную информацию см. в authentication documentation.
PermissionDenied¶
Подпись: PermissionDenied(detail=None, code=None)
Возникает, когда аутентифицированный запрос не прошел проверку на разрешение.
По умолчанию это исключение приводит к ответу с кодом состояния HTTP «403 Forbidden».
NotFound¶
Подпись: NotFound(detail=None, code=None)
Возникает, когда ресурс не существует по заданному URL. Это исключение эквивалентно стандартному исключению Django Http404.
По умолчанию это исключение приводит к ответу с кодом состояния HTTP «404 Not Found».
MethodNotAllowed¶
Подпись: MethodNotAllowed(method, detail=None, code=None)
Возникает, когда происходит входящий запрос, который не сопоставлен с методом-обработчиком на представлении.
По умолчанию это исключение приводит к ответу с кодом состояния HTTP «405 Method Not Allowed».
Неприемлемо¶
Подпись: NotAcceptable(detail=None, code=None)
Возникает, когда поступает запрос с заголовком Accept, который не может быть удовлетворен ни одним из доступных рендереров.
По умолчанию это исключение приводит к ответу с кодом состояния HTTP «406 Not Acceptable».
Дросселированный¶
Подпись: Throttled(wait=None, detail=None, code=None)
Возникает, когда входящий запрос не проходит проверку на дросселирование.
По умолчанию это исключение приводит к ответу с кодом состояния HTTP «429 Too Many Requests».
ValidationError¶
Подпись: ValidationError(detail, code=None)
Исключение ValidationError несколько отличается от других классов APIException:
-
Аргумент
detailявляется обязательным, а не опциональным. -
Аргумент
detailможет представлять собой список или словарь сведений об ошибках, а также может быть вложенной структурой данных. Используя словарь, вы можете указать ошибки на уровне полей при выполнении проверки на уровне объектов в методеvalidate()сериализатора. Например.raise serializers.ValidationError({'name': 'Please enter a valid name.'}) -
По соглашению вы должны импортировать модуль serializers и использовать полностью квалифицированный стиль
ValidationError, чтобы отличить его от встроенной ошибки валидации Django. Например.raise serializers.ValidationError('This field must be an integer value.')
Класс ValidationError следует использовать для сериализатора и валидации полей, а также классами валидаторов. Он также возникает при вызове serializer.is_valid с аргументом ключевого слова raise_exception:
serializer.is_valid(raise_exception=True)
Общие представления используют флаг raise_exception=True, что означает, что вы можете переопределить стиль ответов на ошибки валидации глобально в вашем API. Для этого используйте пользовательский обработчик исключений, как описано выше.
По умолчанию это исключение приводит к ответу с кодом состояния HTTP «400 Bad Request».
Общие представления ошибок¶
Django REST Framework предоставляет два представления ошибок, подходящих для предоставления общих JSON 500 Server Error и 400 Bad Request ответов. (Стандартные представления ошибок Django предоставляют ответы в формате HTML, что может не подойти для приложения, использующего только API).
Используйте их в соответствии с Django’s Customizing error views documentation.
rest_framework.exceptions.server_error¶
Возвращает ответ с кодом состояния 500 и типом содержимого application/json.
Установить как handler500 :
handler500 = 'rest_framework.exceptions.server_error'
rest_framework.exceptions.bad_request¶
Возвращает ответ с кодом состояния 400 и типом содержимого application/json.
Установить как handler400 :
handler400 = 'rest_framework.exceptions.bad_request'
Вернуться на верх
Исключения
Исключения… позволяют чисто организовать обработку ошибок в центральном или высокоуровневом месте в структуре программы.
Обработка исключений в представлениях DRF
Обработка исключений в представлениях DRF
Представления DRF обрабатывают различные исключения и возвращают соответствующие ответы на ошибки.
Обрабатываемыми исключениями являются:
-
Подклассы
APIException, возникающие внутри DRF. -
Исключение Django
Http404. -
Исключение Django
PermissionDenied.
В каждом случае DRF вернет ответ с соответствующим кодом состояния и типом содержимого. В теле ответа будут содержаться любые дополнительные сведения о характере ошибки.
Большинство ответов на ошибки будут содержать ключ detail в теле ответа.
Например, следующий запрос:
DELETE http://api.example.com/foo/bar HTTP/1.1
Может быть получен ответ об ошибке, указывающий на то, что метод DELETE не разрешен для данного ресурса:
HTTP/1.1 405 Method Not Allowed
Content-Type: application/json
{«detail»: «Method ‘DELETE’ not allowed.»}
Ошибки валидации обрабатываются несколько иначе, и в качестве ключей в ответе будут указаны имена полей. Если ошибка валидации не относится к конкретному полю, то будет использоваться ключ «non_field_errors», или любое строковое значение, установленное для параметра NON_FIELD_ERRORS_KEY.
Пример ошибки валидации может выглядеть следующим образом:
Content-Type: application/json
{«amount»: [«A valid integer is required.»], «description»: [«This field may not be blank.»]}
Пользовательская обработка исключений
Пользовательская обработка исключений
Вы можете реализовать пользовательскую обработку исключений, создав функцию-обработчик, которая преобразует исключения, возникающие в ваших представлениях API, в объекты ответа. Это позволяет вам контролировать стиль ответов на ошибки, используемый вашим API.
Функция должна принимать пару аргументов, первый из которых — обрабатываемое исключение, а второй — словарь, содержащий любой дополнительный контекст, например, обрабатываемое в данный момент представление. Функция обработчика исключения должна либо возвращать объект Response, либо возвращать None, если исключение не может быть обработано. Если обработчик возвращает None, то исключение будет повторно поднято, и Django вернет стандартный ответ HTTP 500 "Server error".
Например, вы можете захотеть убедиться, что все ответы на ошибки включают код состояния HTTP в теле ответа, например, так:
HTTP/1.1 405 Method Not Allowed
Content-Type: application/json
{«status_code»: 405, «detail»: «Method ‘DELETE’ not allowed.»}
Чтобы изменить стиль ответа, вы можете написать следующий пользовательский обработчик исключений:
from rest_framework.views import exception_handler
def custom_exception_handler(exc, context):
# Call REST framework’s default exception handler first,
# to get the standard error response.
response = exception_handler(exc, context)
# Now add the HTTP status code to the response.
response.data[‘status_code’] = response.status_code
Аргумент context не используется обработчиком по умолчанию, но может быть полезен, если обработчику исключений нужна дополнительная информация, например, обрабатываемое в данный момент представление, доступ к которому можно получить как context['view'].
Обработчик исключений также должен быть настроен в ваших настройках, используя ключ настройки EXCEPTION_HANDLER. Например:
‘EXCEPTION_HANDLER’: ‘my_project.my_app.utils.custom_exception_handler’
Если параметр 'EXCEPTION_HANDLER' не указан, по умолчанию используется стандартный обработчик исключений, предоставляемый DRF:
‘EXCEPTION_HANDLER’: ‘rest_framework.views.exception_handler’
Обратите внимание, что обработчик исключений будет вызываться только для ответов, сгенерированных поднятыми исключениями. Он не будет использоваться для ответов, возвращаемых непосредственно представлением, таких как ответы HTTP_400_BAD_REQUEST, которые возвращаются общими представлениями при неудачной проверке сериализатора.
Сигнатура: APIException().
базовый класс для всех исключений, возникающих внутри класса APIView или @api_view.
Чтобы предоставить пользовательское исключение, подкласс APIException и установите атрибуты .status_code, .default_detail и default_code для класса.
Например, если ваш API полагается на сторонний сервис, который иногда может быть недоступен, вы можете захотеть реализовать исключение для кода ответа HTTP «503 Service Unavailable». Это можно сделать следующим образом:
from rest_framework.exceptions import APIException
class ServiceUnavailable(APIException):
default_detail = ‘Service temporarily unavailable, try again later.’
default_code = ‘service_unavailable’
Существует ряд различных свойств, доступных для проверки состояния исключения API. Вы можете использовать их для создания пользовательской обработки исключений для вашего проекта.
Доступными атрибутами и методами являются:
-
.detail— Возвращает текстовое описание ошибки. -
.get_codes()— Возвращает идентификатор кода ошибки. -
.get_full_details()— Возвращает как текстовое описание, так и идентификатор кода.
В большинстве случаев деталь ошибки будет простым элементом:
You do not have permission to perform this action.
>>> print(exc.get_codes())
>>> print(exc.get_full_details())
{‘message’:‘You do not have permission to perform this action.’,‘code’:‘permission_denied’}
В случае ошибок валидации деталь ошибки будет представлять собой либо список, либо словарь элементов:
{«name»:«This field is required.»,«age»:«A valid integer is required.»}
>>> print(exc.get_codes())
{«name»:«required»,«age»:«invalid»}
>>> print(exc.get_full_details())
{«name»:{«message»:«This field is required.»,«code»:«required»},«age»:{«message»:«A valid integer is required.»,«code»:«invalid»}}
Сигнатура: ParseError(detail=None, code=None).
Возникает, если запрос содержит неправильно сформированные данные при доступе к request.data.
По умолчанию это исключение приводит к ответу с кодом состояния HTTP «400 Bad Request».
Сигнатура: AuthenticationFailed(detail=None, code=None).
Возникает, когда входящий запрос содержит неправильную аутентификацию.
По умолчанию это исключение приводит к ответу с кодом состояния HTTP «401 Unauthenticated», но оно также может привести к ответу «403 Forbidden», в зависимости от используемой схемы аутентификации. Более подробную информацию см. в документации
authentication documentation
.
Сигнатура: NotAuthenticated(detail=None, code=None).
Возникает, когда неаутентифицированный запрос не прошел проверку на разрешение.
По умолчанию это исключение приводит к ответу с кодом состояния HTTP «401 Unauthenticated», но оно также может привести к ответу «403 Forbidden», в зависимости от используемой схемы аутентификации. Более подробную информацию см. в документации
authentication documentation
.
Сигнатура: PermissionDenied(detail=None, code=None).
Возникает, когда аутентифицированный запрос не прошел проверку на разрешение.
По умолчанию это исключение приводит к ответу с кодом состояния HTTP «403 Forbidden».
Сигнатура: NotFound(detail=None, code=None).
Возникает, когда ресурс не существует по указанному URL. Это исключение эквивалентно стандартному исключению Http404 Django.
По умолчанию это исключение приводит к ответу с кодом состояния HTTP «404 Not Found».
Сигнатура: MethodNotAllowed(method, detail=None, code=None).
Возникает, когда происходит входящий запрос, который не сопоставлен с методом-обработчиком на представлении.
По умолчанию это исключение приводит к ответу с кодом состояния HTTP «405 Method Not Allowed».
Сигнатура: NotAcceptable(detail=None, code=None).
Возникает, когда поступает запрос с заголовком Accept, который не может быть удовлетворен ни одним из доступных рендереров.
По умолчанию это исключение приводит к ответу с кодом состояния HTTP «406 Not Acceptable».
Сигнатура: UnsupportedMediaType(media_type, detail=None, code=None).
Возникает, если при обращении к request.data нет парсеров, способных обработать тип содержимого данных запроса.
По умолчанию это исключение приводит к ответу с кодом состояния HTTP «415 Unsupported Media Type».
Сигнатура: Throttled(wait=None, detail=None, code=None).
Возникает, когда входящий запрос не проходит проверку на дросселирование.
По умолчанию это исключение приводит к ответу с кодом состояния HTTP «429 Too Many Requests».
Сигнатура: ValidationError(detail=None, code=None).
Исключение ValidationError немного отличается от других классов APIException:
-
Аргумент
detailможет быть списком или словарем деталей ошибки, а также может быть вложенной структурой данных. Используя словарь, вы можете указать ошибки на уровне полей при выполнении проверки на уровне объектов в методеvalidate()сериализатора. Например.raise serializers.ValidationError({'name': 'Please enter a valid name.'}). -
По соглашению вы должны импортировать модуль
serializersи использовать полностью определенныйValidationError, чтобы отличить его от встроенной ошибки валидации Django. Например.raise serializers.ValidationError('Это поле должно быть целочисленным значением.').
Класс ValidationError должен использоваться для сериализатора и валидации полей, а также классами валидаторов. Он также вызывается при вызове serializer.is_valid с именованным аргументом raise_exception:
serializer.is_valid(raise_exception=True)
Общие представления используют флаг raise_exception=True, что означает, что вы можете переопределить стиль ответов на ошибки валидации глобально в вашем API. Для этого используйте пользовательский обработчик исключений, как описано выше.
По умолчанию это исключение приводит к ответу с кодом состояния HTTP «400 Bad Request».
Общие представления об ошибках
Общие представления об ошибках
DRF предоставляет два представления ошибок, подходящих для предоставления общих JSON ответов 500 Server Error и 400 Bad Request. (Стандартные представления ошибок Django предоставляют HTML-ответы, которые могут не подойти для приложения, использующего только API).
rest_framework.exceptions.server_error
rest_framework.exceptions.server_error
Возвращает ответ с кодом состояния 500 и типом содержимого application/json.
Устанавливается как handler500:
handler500 = ‘rest_framework.exceptions.server_error’
rest_framework.exceptions.bad_request
rest_framework.exceptions.bad_request
Возвращает ответ с кодом статуса 400 и типом содержимого application/json.
Устанавливается как handler400:
handler400 = ‘rest_framework.exceptions.bad_request’
Пакеты сторонних производителей
Пакеты сторонних производителей
Также доступны следующие пакеты сторонних производителей.
Стандартизированные ошибки ДРФ
Стандартизированные ошибки ДРФ
Пакет
drf-standardized-errors
предоставляет обработчик исключений, который генерирует одинаковый формат для всех ответов 4xx и 5xx. Он является заменой стандартного обработчика исключений и позволяет настраивать формат ответа на ошибку без переписывания всего обработчика исключений. Стандартизированный формат ответа на ошибку легче документировать и проще обрабатывать потребителям API.
- 1. Кастомизация ошибок 404, 500
- 2. Кастомизация ошибки 403
Многие ресурсы имеют оформленные страницы ошибок, если происходит сбой в обработке запроса от клиента.
Для начала на сайте была сделана кастомизация наиболее часто возникающих ошибок, другие при отладке пока не попадались, но всё впереди.
Как объявлено в заголовке статьи, кастомизированы был следующие ошибки:
- 403 — Ошибка авторизации, доступ запрещён.
- 404 — Страница не найдена;
- 500 — Внутренняя ошибка сервера;
Кастомизация ошибок 404, 500
Для кастомизации ошибок 404 и 500 необходимо написать обработчики запросов, и достаточно написать их представления в виде метода.
В шаблоны добавляем свои кастомизированные html файлы, то есть:
- error404.html
- error500.html
Модуль, в котором реализованы представления для данного сайта — это
home.
В шаблонах этого же модуля помещены сами шаблоны кастомизированных ошибок.
В файле
urls.py
главного модуля сайта переопределяем обработчики по умолчанию:
- handler404
- handler500
В коде это выглядит так:
from home.views import e_handler404, e_handler500 handler404 = e_handler404 handler500 = e_handler500Опишем представления в файле
views.py
модуля
home:
from django.shortcuts import render_to_response from django.template import RequestContext def e_handler404(request): context = RequestContext(request) response = render_to_response('error404.html', context) response.status_code = 404 return response def e_handler500(request): context = RequestContext(request) response = render_to_response('error500.html', context) response.status_code = 500 return responseКастомизация ошибки 403
Ошибка 403 возникает в том случае, когда не авторизованный пользователь пытается получить доступ к той части сайта, в которую доступ разрешён только авторизованным пользователям.
В Django это достигается за счёт проверки статуса пользователя и добавления на страницы защитного токена, механизм
CSRF.
Данная ошибка может возникнуть и в том случае, если пользователь авторизован, но совершает действия, при которых требуется проверка токена CSRF, а сам токен был потерян или не верен. Дело в том, что для корректности работы токена, необходимо добавлять в шаблоне в формах специальный тег:{% csrf_token %}В него и будет подставляться токен, но просто добавить в шаблон, его не достаточно. Прежде, чем начать рендер шаблон, необходимо добавить токен в контекст, который будет передан в шаблон. То есть,
from django.template.context_processors import csrf from django.shortcuts import render_to_response def any_request(request): context = {} context.update(csrf(request)) ... return render_to_response('any_request.html', context=context)Ну а теперь ближе к непосредственно кастомизации. Для работы csrf необходимо, чтобы в файле
settings.py
добавлен модуль csrf и указано представление, которое будет заниматься обработкой данной ошибки:MIDDLEWARE = [ ... 'django.middleware.csrf.CsrfViewMiddleware', ... ] CSRF_FAILURE_VIEW = 'home.views.csrf_failure'В шаблонах добавим
error403.html,
а в файле
views.py
пропишем обработчик представления.def csrf_failure(request, reason=""): context = RequestContext(request) response = render_to_response('error403.html', context) response.status_code = 403 return responseДля
Django
рекомендуюVDS-сервера хостера Timeweb
.
В зависимости от задач можно по-разному переопределить в Django стандартное поведение на реакцию ошибок, таких как 403, 404, 500 и стандартные шаблоны вывода ошибок. В официальной документации (на рус. яз. — http://djbook.ru/rel1.8/topics/http/views.html) хорошо это описывается, но тем не менее есть некоторые нюансы, о которых я хочу рассказать и показать в примерах.
В большинстве случаев можно оставить стандартное поведение Django, т. е. стандартные views (из django.views.defaults), обрабатывающие ошибки и переопределить только шаблоны.
Допустим, у нас есть шаблон ошибки 404:
# 404.html
{% extends "base.html" %}
{% block subtitle %}Страница не найдена{% endblock %}
{% block meta %}<meta name="robots" content="noindex, nofollow">{% endblock %}
{% block content %}
<h2>Запрашиваемая страница не найдена</h2>
<p>Возможно, неправильно указан путь в адресной строке или страница была удалена.</p>
<p>Возврат на <a href="/">главную страницу</a></p>
{% endblock %}
А ниже описаны разные способы переопределения.
Добавление своего шаблона в templates
Самый простой способ переопределения — это добавить этот шаблон в «my_project / templates». Именно здесь django будет искать шаблон 404.html и при наличии отрендерит.
Добавление своего шаблона в другую папку
Не всегда удобно держать шаблоны ошибок в «my_project / templates» вместе с какими-то другими шаблонами и хочется их вынести в отдельную папку. Я, к примеру, использую следующую структуру папки templates в корне проекта (жирным выделены папки):
- admin — содержит переопределяемые админские шаблонов
- cms — содержит переопределяемые шаблоны Django CMS
- errs — содержит шаблоны ошибок
- 403.html
- 404.html
- 500.html
- vers — содержит проверочные файлы для поисковых систем
- base.html
- one_col.html
- two_col.html
- main.html
Как видите, вынос шаблонов ошибок в отдельную папку — хорошая идея отделить их от шаблонов страниц (one_col.html, two_col.html, main.html). Чтобы указать django, где искать шаблоны по новому пути, нужно использовать метод curry, например:
# urls.py from django.views.defaults import server_error, page_not_found, permission_denied handler403 = curry(permission_denied, template_name='errs/403.html') handler404 = curry(page_not_found, template_name='errs/404.html') handler500 = curry(server_error, template_name='errs/500.html')
Замечание: в Django>=1.9 нужно добавить ещё один аргумент exception для handler403 и handler404:
handler403 = curry(permission_denied, exception=Exception('Permission Denied'), template_name='errs/403.html')
handler404 = curry(page_not_found, exception=Exception('Page not Found'), template_name='errs/404.html')
Тестирование своих шаблонов ошибок
Чтобы протестировать шаблоны ошибок, достаточно добавить соответствующие urls в urls.py, например так:
if settings.DEBUG:
urlpatterns = [
url(r'^media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT, 'show_indexes': True}),
url(r'', include('django.contrib.staticfiles.urls')),
# --- error pages ---
url(r'^403$', handler403),
url(r'^404$', handler404),
url(r'^500$', handler500),
] + urlpatterns
Теперь можно переходить по адресу http://localhost:8000/404 и видеть свой кастомный шаблон ошибки. На боевом сайте нам тестовые урлы ошибок не нужны, поэтому мы добавили их в условие if settings.DEBUG:.
Создание своего представления для обработки ошибки
Если хочется изменить стандартное поведение обработки ошибок, то нужно писать своё представление. Например:
# urls.py
handler403 = 'my_app.views.show_403'
handler404 = 'my_app.views.show_404'
# my_app/views.py
# Обработка ошибки 403
from django.core.exceptions import PermissionDenied
def show_403(request):
# какие-либо действия
raise PermissionDenied
# Обработка ошибки 404
from django.http.response import Http404
def show_404(request):
# какие-либо действия
raise Http404
Можно усложнить пример с обработкой ошибки 404. Например, мы хотим отдавать разные шаблоны 404 в зависимости от того, с чего начинается отсутствующий url:
# Обработка ошибки 404
from django.http.response import Http404
def 404(request):
if request.path.startswith('/project/'):
return render(request, 'project_not_found.html') # выдаст страницу, что проекта нет и, к примеру, покажет другие проекты
if request.path.startswith('/shop/'):
return render(request, 'product_not_found.html') # выдаст страницу, что товара нет и, к примеру, покажет другие товары
raise Http404 # в остальных случаях показать стандартное исключение, которое отрендерит наш шаблон 404.html
Некоторые тонкости
Проверка отдачи шаблона 404
Для проверки отдачи шаблона 404 используйте при значении переменной DEBUG=False в settings.py, иначе вам будет показан трейсбек Django.
Внимание! Если DEBUG=False, то вы должны добавить в ALLOWED_HOSTS (в settings.py) допустимые доменные имена, иначе Django будет выдавать ошибку “Bad Request (400)”. На локальной машине добавляется ‘localhost’ (или 127.0.0.1).
# settings.py ALLOWED_HOSTS = ['localhost', ]
Обратите внимание, что при DEBUG=False статические файлы не будут показываться. Для показа статических файлов нужно собрать статику и запустить сервер с опцией --insecure:
python manage.py collectstatic python manage.py runserver --insecure
Создание своего представления для обработки 500 ошибки
При использовании стандартного представления обработки 500 ошибки в соответствующий шаблон не передаётся контекст. Сделано так для того, чтобы уменьшить количество возможных других ошибок. Поэтому шаблон должен быть простой подобный этому:
<!DOCTYPE html>
<html>
<head>
<title>Ошибка на стороне сервера</title>
<meta name="robots" content="noindex, nofollow">
</head>
<body>
<p>Извините, но что-то случилось с сайтом.</p>
<p>В техническую поддержку уже отправлено уведомление.</p>
</body>
</html>
Если будете делать своё представление для 500 ошибки, то следуйте правилам django — не передавайте RequestContext при рендеринге шаблона. Посмотрите как происходит обработка в стандартном представлении django.views.defaults.server_error.
Отображение ошибок без рендеринга шаблонов
from django.http import HttpResponseNotFound
def test_view_1(request, param):
if not param: # какое-то условие
return HttpResponseNotFound('<p>Страница не найдена</p>')
return render_to_response('test_view_1.html')
from django.http.response import HttpResponseForbidden
def test_view_2(request, param):
if not param:
return HttpResponseForbidden('Доступ запрещён')
return render_to_response('test_view_2.html')
Надеюсь, статья помогла ответить на возникающие вопросы об обработке ошибок в Django.
Оцените статью
3.9 из 5 (всего 7 оценок)
После нажатия кнопки «Отправить» ваше сообщение будет доставлено мне на почту.
Артём Мальцев
Веб-разработчик, владеющий знаниями языка программирования Python, фреймворка Django, системы управления содержимым сайта Django CMS, платформы для создания интернет-магазина Django Shop и многих различных приложений, использующих эти технологии.
Права на использование материала, расположенного на этой странице https://vivazzi.pro/ru/it/django-custom-templates-for-errors/:
Разрешается копировать материал с указанием её автора и ссылки на оригинал без использования параметра rel="nofollow" в теге <a>. Использование:
Автор статьи: Артём Мальцев
Ссылка на статью: <a href="https://vivazzi.pro/ru/it/django-custom-templates-for-errors/">https://vivazzi.pro/ru/it/django-custom-templates-for-errors/</a>
Больше: Правила использования сайта
Представляю вашему вниманию книгу, написанную моим близким другом Максимом Макуриным: Секреты эффективного управления ассортиментом.
Книга предназначается для широкого круга читателей и, по мнению автора, будет полезна специалистам отдела закупок и логистики, категорийным и финансовым менеджерам, менеджерам по продажам, аналитикам, руководителям и директорам, в компетенции которых принятие решений по управлению ассортиментом.


