【问题标题】:Passing session from template view to python requests api call将会话从模板视图传递到 python 请求 api 调用
【发布时间】:2016-11-21 09:54:51
【问题描述】:

我想使用请求库从我的 Django TemplateView 进行多个内部 REST API 调用。现在我也想将会话从模板视图传递到 api 调用。考虑到性能,推荐的方法是什么。

现在,我正在从模板视图中的当前request 对象中提取cookie,并将其传递给requests.get()requests.post() 调用。但问题是,我必须将 request 对象传递给我的 API 客户端,这是我不想要的。

这是我用来路由请求的当前包装器:

def wrap_internal_api_call(request, requests_api, uri, data=None, params=None, cookies=None, is_json=False, files=None):
    headers = {'referer': request.META.get('HTTP_REFERER')}
    logger.debug('Request API: %s calling URL: %s', requests_api, uri)
    logger.debug('Referer header sent with requests: %s', headers['referer'])
    if cookies:
        csrf_token = cookies.get('csrftoken', None)
    else:
        csrf_token = request.COOKIES.get('csrftoken', None)

    if csrf_token:
        headers['X-CSRFToken'] = csrf_token
    if data:
        if is_json:
            return requests_api(uri, json=data, params=params, cookies=cookies if cookies else request.COOKIES, headers=headers)
        elif not files:
            return requests_api(uri, data=data, params=params, cookies=cookies if cookies else request.COOKIES, headers=headers)
        else:
            return requests_api(uri, data=data, files=files, params=params, cookies=cookies if cookies else request.COOKIES,
                                headers=headers)
    else:
        return requests_api(uri, params=params, cookies=cookies if cookies else request.COOKIES, headers=headers)

基本上,我想摆脱 request 参数(第一个参数),因为要调用它,我必须继续将 request 对象从 TemplateViews 传递给内部服务。另外,如何在多个调用中保持持久连接?

【问题讨论】:

  • 几个问题:您是如何接收数据的?您一般想要完成什么?这些是出站 API 调用吗?你需要传递整个会话对象吗?似乎是一个不寻常的用例,但我需要看到更大的图景。您能否解释一下要完成的工作。谢谢。
  • @mariodev:我有一个 Django TemplateView。我想收集数据以放入该视图的上下文中。为此,我将调用一个 REST API,它再次出现在我的应用程序中。但是进行 REST API 调用将是一个新调用,因此如果我不通过cookie,那么session 将与我不想要的 TemplateView 中的不同。基本上我想保持身份验证对内部 API 调用有效。
  • @mariodev: data 只是一个 python 字典。
  • 每当处理 api 请求时,最好为 auth 传递令牌(在这种情况下,像 JWT 这样的东西应该可以工作)。虽然我仍然很困惑,你为什么要使用 api 调用?为什么不直接使用指向接收端点的内部函数或类方法呢?正如您声称的那样,您只在内部使用它,所以我认为没有必要调用 api 真的..
  • 您是否有理由不能只从视图中调用其余 API 函数并将数据传递给您的模板上下文? Django 在请求、视图、模板、响应的循环中运行。您正在尝试在此处添加一个步骤,并使其请求、视图、模板、视图、响应,这从来都不是堆栈的设计方式。

标签: python django


【解决方案1】:

基本上我想摆脱那个请求参数(第一个参数),因为要调用它,我必须继续将请求对象从 TemplateViews 传递给内部服务。

要传递函数参数而不显式地将它们传递给函数调用,你可以使用装饰器来包装你的函数并自动注入你的参数。在请求到达您的视图之前将其与全局变量和一些 django 中间件一起使用将解决您的问题。有关我的意思的抽象和简化版本,请参见下文。

request_decorators.py

REQUEST = None


def request_extractor(func):

    def extractor(cls, request, *args, **kwargs):
        global REQUEST
        REQUEST = request # this part registers request arg to global
        return func(cls, request, *args, **kwargs) 

    return extractor


def request_injector(func):

    def injector(*args, **kwargs):
        global REQUEST
        request = REQUEST
        if len(args) > 0 and callable(args[0]): # to make it work with class methods
            return func(args[0], request, args[1:], **kwargs) # class method
        return func(request, *args, **kwargs) # function

    return injector

extract_request_middleware.py

See the django docs for info on setting up middleware

from request_decorators import request_extractor

class ExtractRequest:

    @request_extractor
    def process_request(self, request):
        return None

internal_function.py

from request_decorators import request_injector

@request_injector
def internal_function(request):
    return request

your_view.py

from internal_function import internal_function

def view_with_request(request):
    return internal_function() # here we don't need to pass in the request arg.

def run_test():

    request = "a request!"
    ExtractRequest().process_request(request)
    response = view_with_request(request)
    return response


if __name__ == '__main__':

    assert run_test() == "a request!"

【讨论】:

  • 要么,完全不清楚您的答案如何解决问题,要么您的答案根本没有抓住重点。对我来说,它看起来像是一种隐蔽传递参数的一般技巧,即不可见。再说一次,这甚至在您的示例代码中似乎都不起作用。无论如何,使参数“不可见”不是@RohitJain 要求的。
【解决方案2】:

REST 与直接调用视图

虽然网络应用可以对自身进行 REST API 调用。这不是 REST 的设计目的。考虑以下来自:https://docs.djangoproject.com/ja/1.9/topics/http/middleware/

如您所见,django 请求/响应周期有相当多的开销。再加上 webserver 和 wsgi 容器的开销。在客户端,您有与请求库相关的开销,但稍等片刻,客户端也恰好是同一个 Web 应用程序,因此它也成为 Web 应用程序开销的一部分。还有持久性的问题(我很快就会谈到)。

最后但并非最不重要的一点是,如果您有 DNS 循环设置,您的请求实际上可能会在返回到同一台服务器之前通过网络发送出去。有一种更好的方法,可以直接调用视图。

无需其他 API 调用即可调用另一个视图非常简单

 other_app.other_view(request, **kwargs)

这已经在Django Call Class based view from another class based viewCan I call a view from within another view?等链接上讨论过几次,所以我不会详细说明。

持续请求

持久的http请求(谈论python请求而不是django.http.request.HttpRequest)是通过会话对象管理的(同样不要与django会话混淆)。避免混淆真的很困难:

Session 对象允许你在 要求。它还在从 会话实例,并将使用 urllib3 的连接池。因此,如果 您正在向同一主机发出多个请求,即底层 TCP 连接将被重用,这可能会导致显着 性能提升

对您的 django 视图的不同点击可能来自不同的用户,因此您不希望将相同的 cookie 重用于内部 REST 调用。另一个问题是 python 会话对象不能在 django 视图的两个不同命中之间持久化。套接字通常不能被序列化,需要将它们放入 memcached 或 redis 中。

如果你仍然想坚持内部 REST

我认为@julian 的回答显示了如何避免将 django 请求实例作为参数传递。

【讨论】:

  • 所以我尝试了直接视图调用方法,想知道如何将查询参数传递给视图?
  • 您正在传递整个请求对象。所以第二个视图可以从 request.GET 中获取
  • 酷。其实我想发送不同的查询参数。所以我修改了request.GET 对象。但是as_view() 似乎返回了TemplateResponse。我如何从中取出json?通过 API 调用,我只是做了response.json() 来获取。现在当我做response.render().content 时,它是模板响应。
  • 如果视图返回json,json_loads(response.rendered_content)
【解决方案3】:

如果您想避免将request 传递给wrap_internal_api_call,您只需在调用 api 包装器的 TemplateView 末尾做更多的工作。请注意,您的原始包装器做了很多cookies if cookies else request.COOKIES。您可以将其分解到呼叫站点。重写你的 api 包装器如下:

def wrap_internal_api_call(referer, requests_api, uri, data=None, params=None, cookies, is_json=False, files=None):
    headers = {'referer': referer}
    logger.debug('Request API: %s calling URL: %s', requests_api, uri)
    logger.debug('Referer header sent with requests: %s', referer)
    csrf_token = cookies.get('csrftoken', None)

    if csrf_token:
        headers['X-CSRFToken'] = csrf_token
    if data:
        if is_json:
            return requests_api(uri, json=data, params=params, cookies=cookies, headers=headers)
        elif not files:
            return requests_api(uri, data=data, params=params, cookies=cookies, headers=headers)
        else:
            return requests_api(uri, data=data, files=files, params=params, cookies=cookies, headers=headers)
    else:
        return requests_api(uri, params=params, cookies=cookies, headers=headers)

现在,在调用的地方,而不是

wrap_internal_api_call(request, requests_api, uri, data, params, cookies, is_json, files)

做:

cookies_param = cookies or request.COOKIES
referer_param = request.META.get['HTTP_REFERER']
wrap_internal_api_call(referer_param, requests_api, uri, data, params, cookies_param, is_json, files)

现在您不再将request 对象传递给包装器。这节省了一点时间,因为您不会一遍又一遍地测试cookies,否则它不会对性能产生影响。事实上,您只需在 inside api 包装器中执行 cookies or request.COOKIES 即可获得相同的轻微性能提升。

网络始终是任何应用程序中最严重的瓶颈。因此,如果这些内部 API 与您的 TemplateView 位于同一台机器上,那么您最好的性能选择是避免进行 API 调用。

【讨论】:

    猜你喜欢
    • 2018-08-12
    • 1970-01-01
    • 2017-06-23
    • 2020-08-27
    • 2015-09-18
    • 2018-03-23
    • 2015-07-07
    • 2018-02-10
    • 2016-10-28
    相关资源
    最近更新 更多