在Django中将请求响应转换为DRF响应的最优雅方法是什么?

时间:2017-08-10 09:51:23

标签: python django django-rest-framework

考虑以下流程:

public client ----> DRF API on Service A ------> DRF API on Service B

服务A上的某些DRF API仅代理服务B,因此在服务A上的特定API中如下所示:

class SomeServiceAPI(APIView):
    def get(request):
        resp = requests.get('http://service-b.com/api/...')
        return Response(resp.json())

虽然这适用于正常状态,但它有一些问题:

  1. 它不代表服务b的实际状态代码。
  2. Response()
  3. 中不必要的json序列化往返
  4. 如果服务b返回非json错误,则服务不会从服务b返回实际错误。
  5. 问题是,有更好的方法吗?我查看了Django Rest Framework Proxy项目,但我不完全确定它是否真的适合我的用例。

2 个答案:

答案 0 :(得分:3)

  1. 您可以通过修改Response

    来解决状态码部分
    return Response(resp.json(), status=resp.status_code)
    
  2. 对于第二部分,这是Proxying的本质......(的确,有时你想在代理的中间人中操纵请求和/或响应,但你所做的就是本质)。

  3. 注意:

    • 您建议的DRF Proxy似乎只是做了这项工作 很好,不需要你只为了写一个特定的视图 往返。
    • 还有另一个工具DRF Reverse Proxy,它是Django Revproxy的DRF端口,您可能需要考虑。

    以上两者的一般概念是您创建一个专门用于代理另一个API路径的URL路径:

    DRF代理:

    将您的代理添加到settings.py

    REST_PROXY = {
        'HOST': 'http://service-b.com/api/'
    }
    

    urls.py

    url(
        r'^somewere_in_a/$', 
        ProxyView.as_view(source='somewere_in_b/'), 
        name='a_name'
    ) 
    

    DRF反向代理:

    与上述非常相似,没有设置部分:

    url(
        r'^(?P<path>.*)$', 
        ProxyView.as_view(upstream='http://service-b.com/api/somewere_in_b/'),
        name='a_name'
    )
    

    意见: DRF Proxy似乎更加稳固......

答案 1 :(得分:2)

我查看了John的答案中提到的现有软件包,但它们似乎并不完全适合我的用例,因此我创建了一个简单的包装器来代理请求。对DRF回应的回应。

# encoding: utf-8
from __future__ import unicode_literals
from __future__ import absolute_import
from rest_framework.response import Response
from requests.models import Response as RResponse


class InCompatibleError(Exception):
    pass


class DRFResponseWrapper(Response):
    """
    Wraps the requests' response
    """
    def __init__(self, data, *args, **kwargs):
        if not isinstance(data, RResponse):
            raise InCompatibleError

        status = data.status_code
        content_type = data.headers.get('content_type')

        try:
            content = data.json()
        except:
            content = data.content

        super(DRFResponseWrapper, self).__init__(content, status=status, content_type=content_type)

使用如下:

    resp = requests.get(
        '{}://{}/api/v5/business/'.format(settings.SEARCH_HOST_SCHEMA, settings.SEARCH_HOST),
        params=request.query_params
    )
    return DRFResponseWrapper(resp)