"""
XHTML Degrader Middleware.
When sending contents with the XHTML media type, application/xhtml+xml, this
module checks to ensure that the user agent (web browser) is capable of
rendering it. If not, it changes the media type to text/html and makes the
contents more "HTML-friendly" (as per the XHTML 1.0 HTML Compatibility
Guidelines).
To use this middleware you need to add a reference to it in your settings.py
file, e.g.:
MIDDLEWARE_CLASSES = (
...
'YOURPATH.xhtmldegrader.middleware.XhtmlDegraderMiddleware',
)
(If you use GZipMiddleware, you should ensure that it appears in the list
before XhtmlDegraderMiddleware, to allow the XHTML Degrader to act first.)
"""
import re
_MEDIA_TYPE_RE = re.compile(r'application\/xhtml\+xml')
_EMPTY_TAG_END_RE = re.compile(r'(?<=\S)\/\>')
_PROCESSING_INSTRUCTION_RE = re.compile(r'\<\?.*\?\>')
def _supports_xhtml(request):
"""Examines an HTTP request header to determine whether the user agent
supports the XHTML media type (application/xhtml+xml). Returns True or
False."""
if '/xhtml+xml' in request.META.get('HTTP_ACCEPT', '').lower():
# User agent claims to support the XHTML media type.
return True
else:
# No reference to XHTML support.
return False
class XhtmlDegraderMiddleware(object):
"""Django middleware that "degrades" any contents sent as XHTML if the
requesting browser doesn't support the XHTML media type. Degrading involves
switching the content type to text/html, removing XML processing
instructions, etc.
If the HTTP response is already set to text/html, or set to any media type
other than application/xhtml+xml, this middleware will have no effect.
"""
def process_response(self, request, response):
# Check if this is XHTML, and check if the user agent supports XHTML.
if response['Content-Type'].split(';')[0] != 'application/xhtml+xml' \
or _supports_xhtml(request):
# The content is fine, simply return it.
return response
# If the response has already been compressed we can't modify it
# further, so just return it. (N.B. if you use GZipMiddleware, you
# should ensure that it's listed in MIDDLEWARE_CLASSES before
# XhtmlDegraderMiddleware, to allow the XHTML Degrader to act first.)
if response.has_header('Content-Encoding'):
# Already compressed, so we can't do anything useful with it.
return response
# The content is XHTML, and the user agent doesn't support it.
# Fix the media type:
response['Content-Type'] = _MEDIA_TYPE_RE.sub('text/html',
response['Content-Type'], 1)
if 'charset' not in response['Content-Type']:
response['Content-Type'] += '; charset=utf-8'
# Modify the response contents as required:
# Remove any XML processing instructions:
response.content = _PROCESSING_INSTRUCTION_RE.sub('',
response.content)
# Ensure there's a space before the trailing '/>' of empty elements:
response.content = _EMPTY_TAG_END_RE.sub(' />',
response.content)
# Lose any excess whitespace:
response.content = response.content.strip()
if not response.content.startswith('\n' + response.content
return response