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

Jam*_*Lin 6 python django django-rest-framework

请考虑以下流程:

public client ----> DRF API on Service A ------> DRF API on Service B
Run Code Online (Sandbox Code Playgroud)

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

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

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

  1. 它不代表服务b的实际状态代码.
  2. 在Response()中不必要的json序列化往返
  3. 如果服务b返回非json错误,则服务不会从服务b返回实际错误.

问题是,有更好的方法吗?我看了一下Django Rest Framework Proxy项目,但我不完全确定它是否真的适合我的用例.

Joh*_*fis 6

  1. 您可以通过修改以下内容来解决状态代码部分Response

    return Response(resp.json(), status=resp.status_code)
    
    Run Code Online (Sandbox Code Playgroud)
  2. 不过,对于第二部分,这是代理的本质...(确实,有时您想在代理的中间人中操作请求和/或响应,但您所做的才是本质)。

笔记:

  • 您建议的DRF 代理似乎可以很好地完成这项工作,而无需您仅为往返编写特定视图。
  • 存在另一个工具,DRF 反向代理,它是Django Revproxy的 DRF 端口,您可能需要考虑。

以上两者的总体思路是,您专门创建一个 URL 路径来将路径代理到另一个 API:

DRF 代理:

将您的代理添加到settings.py

REST_PROXY = {
    'HOST': 'http://service-b.com/api/'
}
Run Code Online (Sandbox Code Playgroud)

urls.py

url(
    r'^somewere_in_a/$', 
    ProxyView.as_view(source='somewere_in_b/'), 
    name='a_name'
) 
Run Code Online (Sandbox Code Playgroud)

DRF反向代理:

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

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

观点:DRF代理似乎更加坚实...


Jam*_*Lin 6

我查看了 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)
Run Code Online (Sandbox Code Playgroud)

并使用如下:

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