Django TastyPie:如何为UnsupportedFormat返回500以外的东西?

时间:2014-02-06 16:11:09

标签: python json django http tastypie

使用Django TastyPie,当我向只接受JSON的API提供非JSON请求时,我收到500错误和包含此回溯的repsonse:

Traceback (most recent call last):

  File ".../lib/python3.3/site-packages/tastypie/resources.py", line 195, in wrapper
    response = callback(request, *args, **kwargs)

  File ".../lib/python3.3/site-packages/tastypie/resources.py", line 426, in dispatch_list
    return self.dispatch('list', request, **kwargs)

  File ".../lib/python3.3/site-packages/tastypie/resources.py", line 458, in dispatch
    response = method(request, **kwargs)

  File ".../lib/python3.3/site-packages/tastypie/resources.py", line 1317, in post_list
    deserialized = self.deserialize(request, request.body, format=request.META.get('CONTENT_TYPE', 'application/json'))

  File ".../lib/python3.3/site-packages/tastypie/resources.py", line 375, in deserialize
    deserialized = self._meta.serializer.deserialize(data, format=request.META.get('CONTENT_TYPE', 'application/json'))

  File ".../lib/python3.3/site-packages/tastypie/serializers.py", line 219, in deserialize
    raise UnsupportedFormat("The format indicated '%s' had no available deserialization method. Please check your ``formats`` and ``content_types`` on your Serializer." % format)

tastypie.exceptions.UnsupportedFormat: The format indicated 'application/x-www-form-urlencoded' had no available deserialization method. Please check your ``formats`` and ``content_types`` on your Serializer.

我无意添加对formdata的支持,因此与400(错误请求)或415(不支持的媒体类型)相比,500(内部服务器错误)似乎不合适。但我似乎无法弄清楚你应该如何指定TastyPie来返回这些代码。这是我没有找到的TastyPie的功能,还是我必须手动滚动此功能?

2 个答案:

答案 0 :(得分:0)

第1部分 - 序列化

您可以使用额外的反序列化创建父资源:

class ExtraPostResource(object):
    def deserialize(self, request, data, format=None):
        """
        Changes request stat in to python objects
        """
        if not format:
            format = request.META.get('CONTENT_TYPE', 'application/json')

        if format == 'application/x-www-form-urlencoded':
            return request.POST

        if format.startswith('multipart'):
            multipart_data = request.POST.copy()
            multipart_data.update(request.FILES)
            return multipart_data

        return super(ExtraPostResource, self).deserialize(request, data, format)

然后将其应用于您需要的资源:

class MyModel(ExtraPostResource, ModelResource):
    class Meta:
        serializer = Serializer(formats=['json'])
        queryset = MyModel.objects.all()
        resource_name = 'my_model'

我没有使用application/x-www-form-urlencoded测试示例,但multipart对我来说非常适合。

现在是第2部分 - 约500s

如果你想处理500s。

try:
    from django.views.decorators.csrf import csrf_exempt
except ImportError:
    def csrf_exempt(func):
        return func

class MyBaseModelResource(ModelResource):
    """
    Basically it defines own error feedback format.
    """
    def wrap_view(self, view):
        """
        Wraps views to return custom error codes instead of generic 500's.
        """
        @csrf_exempt
        def wrapper(request, *args, **kwargs):  # * see annotation below.
            try:
                callback = getattr(self, view)
                response = callback(request, *args, **kwargs)

                if request.is_ajax():
                    patch_cache_control(response, no_cache=True)

                return response

            # You can handle here many more.
            # [...]
            except UnsupportedFormat, e: 
                return self.create_response(
                    request, 
                    {'success': False,
                     'code': 123,
                     'message': e},
                     response_class=HttpBadRequest, # HttpForbidden, HttpUnauthorized etc.
                )

            except Exception, e:
            # Rather than re-raising, we're going to things similar to
            # what Django does. The difference is returning a serialized
            # error message.
                 return self._handle_500(request, e)
        return wrapper

*(1)值得一提的是,包装器方法的版本可能与您目前使用的Tastypie版本略有不同或 不同。你必须检查它。

答案 1 :(得分:0)

您必须create your own Serializer class并将其用于资源。

class JsonOnlySerializer(Serializer):

    # limit the available formats
    formats = ['json']
    content_types = {'json': 'application/json'}

    # catch the Unsupported exception and raise BadRequest
    def deserialize(self, content, format='application/json'):
        try:
            return super(SafeJsonSerializer, self).deserialize(content, format)
        except UnsupportedFormat as e:
            raise BadRequest("Unsupported content type (%s)." % format)

然后将其用于资源的配置:

class MyModel(ModelResource):

    class Meta:
        queryset = MyModel.objects.all()
        serializer = JsonOnlySerializer()