This is a first pass at a non-breaking change that'll allow for a customizable media handling system. This approach combines many of the suggestions brought up by the community in #145. One the thing that is left out of this PR is handling full content negotiation (i.e. connecting the request's accept header to the response's content-type). Unfortunately, this is a harder problem to solve in a backwards compatible fashion that doesn't affect performance. However, especially as we move towards v2, I think that would be a great opportunity to revisit full negotiation. In the meantime, there are several easy workarounds for people needing this functionality. Closes #145
55 lines
1.6 KiB
Python
55 lines
1.6 KiB
Python
import mimeparse
|
|
|
|
from six.moves import UserDict
|
|
|
|
from falcon import errors
|
|
from falcon.media import JSONHandler
|
|
|
|
|
|
class Handlers(UserDict):
|
|
"""A dictionary like object that manages internet media type handlers."""
|
|
def __init__(self, initial=None):
|
|
handlers = initial or {
|
|
'application/json': JSONHandler(),
|
|
'application/json; charset=UTF-8': JSONHandler(),
|
|
}
|
|
|
|
# NOTE(jmvrbanac): Directly calling UserDict as it's not inheritable.
|
|
# Also, this results in self.update(...) being called.
|
|
UserDict.__init__(self, handlers)
|
|
|
|
def _resolve_media_type(self, media_type, all_media_types):
|
|
resolved = None
|
|
|
|
try:
|
|
# NOTE(jmvrbanac): Mimeparse will return an empty string if it can
|
|
# parse the media type, but cannot find a suitable type.
|
|
resolved = mimeparse.best_match(
|
|
all_media_types,
|
|
media_type
|
|
)
|
|
except ValueError:
|
|
pass
|
|
|
|
return resolved
|
|
|
|
def find_by_media_type(self, media_type, default):
|
|
# PERF(jmvrbanac): Check via a quick methods first for performance
|
|
if media_type == '*/*' or not media_type:
|
|
return self.data[default]
|
|
|
|
try:
|
|
return self.data[media_type]
|
|
except KeyError:
|
|
pass
|
|
|
|
# PERF(jmvrbanac): Fallback to the slower method
|
|
resolved = self._resolve_media_type(media_type, self.data.keys())
|
|
|
|
if not resolved:
|
|
raise errors.HTTPUnsupportedMediaType(
|
|
'{0} is an unsupported media type.'.format(media_type)
|
|
)
|
|
|
|
return self.data[resolved]
|