diff --git a/README.md b/README.md index 9e7b6f9..dbc031b 100644 --- a/README.md +++ b/README.md @@ -135,6 +135,11 @@ MAINTENANCE_MODE_LOGOUT_AUTHENTICATED_USER = False MAINTENANCE_MODE_REDIRECT_URL = None ``` +```python +# the type of the response returned during maintenance mode, can be either "html" or "json" +MAINTENANCE_MODE_RESPONSE_TYPE = "html" +``` + ```python # the template that will be shown by the maintenance-mode page MAINTENANCE_MODE_TEMPLATE = "503.html" diff --git a/maintenance_mode/http.py b/maintenance_mode/http.py index eb2ea2a..e73900c 100644 --- a/maintenance_mode/http.py +++ b/maintenance_mode/http.py @@ -4,6 +4,7 @@ from django.conf import settings from django.contrib.auth import logout from django.core.exceptions import ImproperlyConfigured +from django.http import JsonResponse from django.shortcuts import redirect, render from django.urls import NoReverseMatch, Resolver404, resolve, reverse from django.utils.cache import add_never_cache_headers @@ -13,15 +14,8 @@ from maintenance_mode.utils import get_client_ip_address -def get_maintenance_response(request): - """ - Return a '503 Service Unavailable' maintenance response. - """ - if settings.MAINTENANCE_MODE_REDIRECT_URL: - return redirect(settings.MAINTENANCE_MODE_REDIRECT_URL) - +def get_maintenance_response_context(request): context = {} - if settings.MAINTENANCE_MODE_GET_TEMPLATE_CONTEXT: try: get_request_context_func = import_string( @@ -34,19 +28,57 @@ def get_maintenance_response(request): ) from error context = get_request_context_func(request=request) + return context + + +def get_maintenance_response(request): + """ + Return a '503 Service Unavailable' HTML or JSON response based on user preference. + """ + if settings.MAINTENANCE_MODE_REDIRECT_URL: + return redirect(settings.MAINTENANCE_MODE_REDIRECT_URL) + + response = None + response_type = settings.MAINTENANCE_MODE_RESPONSE_TYPE + if response_type == "html": + response = get_maintenance_html_response(request) + elif response_type == "json": + response = get_maintenance_json_response(request) + else: + raise ImproperlyConfigured( + "settings.MAINTENANCE_MODE_RESPONSE_TYPE value must be 'html' or 'json'." + ) - kwargs = {"context": context} + if settings.MAINTENANCE_MODE_RETRY_AFTER: + response["Retry-After"] = settings.MAINTENANCE_MODE_RETRY_AFTER + + add_never_cache_headers(response) + return response + + +def get_maintenance_html_response(request): + """ + Return an HTML response for maintenance. + """ + context = get_maintenance_response_context(request) response = render( request, settings.MAINTENANCE_MODE_TEMPLATE, status=settings.MAINTENANCE_MODE_STATUS_CODE, - **kwargs, + context=context, ) + return response - if settings.MAINTENANCE_MODE_RETRY_AFTER: - response["Retry-After"] = settings.MAINTENANCE_MODE_RETRY_AFTER - add_never_cache_headers(response) +def get_maintenance_json_response(request): + """ + Return a JSON response for maintenance. + """ + context = get_maintenance_response_context(request) + response = JsonResponse( + context, + status=settings.MAINTENANCE_MODE_STATUS_CODE, + ) return response diff --git a/maintenance_mode/settings.py b/maintenance_mode/settings.py index 0e79437..a6d0207 100644 --- a/maintenance_mode/settings.py +++ b/maintenance_mode/settings.py @@ -43,6 +43,9 @@ if not hasattr(settings, "MAINTENANCE_MODE_REDIRECT_URL"): settings.MAINTENANCE_MODE_REDIRECT_URL = None +if not hasattr(settings, "MAINTENANCE_MODE_RESPONSE_TYPE"): + settings.MAINTENANCE_MODE_RESPONSE_TYPE = "html" + if not hasattr(settings, "MAINTENANCE_MODE_STATE_BACKEND"): settings.MAINTENANCE_MODE_STATE_BACKEND = ( "maintenance_mode.backends.LocalFileBackend" diff --git a/tests/tests.py b/tests/tests.py index ecd9010..5bc5cea 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -12,6 +12,7 @@ from django.core.exceptions import ImproperlyConfigured from django.core.management import call_command from django.core.management.base import CommandError +from django.http import HttpResponse, JsonResponse from django.test import ( Client, RequestFactory, @@ -1000,6 +1001,26 @@ def test_middleware_get_template_context(self): val = response.context.get("TEST_MAINTENANCE_MODE_GET_TEMPLATE_CONTEXT", False) self.assertFalse(val) + def test_middleware_response_type(self): + self.__reset_state() + + settings.MAINTENANCE_MODE = True + request = self.__get_anonymous_user_request("/") + + settings.MAINTENANCE_MODE_RESPONSE_TYPE = "none" + with self.assertRaises(ImproperlyConfigured): + self.middleware.process_request(request) + + settings.MAINTENANCE_MODE_RESPONSE_TYPE = "json" + response = self.middleware.process_request(request) + self.assertMaintenanceResponse(response) + self.assertTrue(isinstance(response, JsonResponse)) + + settings.MAINTENANCE_MODE_RESPONSE_TYPE = "html" + response = self.middleware.process_request(request) + self.assertMaintenanceResponse(response) + self.assertTrue(isinstance(response, HttpResponse)) + class TestOverrideMaintenanceMode(SimpleTestCase): """