将模板视图中的会话传递给python请求api调用

我想从我的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参数(第一参数),因为然后调用它,我必须保持从TemplateViews传递request对象到内部服务。 另外,如何保持跨多个呼叫的持续连接?


REST vs直接调用视图

虽然Web应用程序可以为其自身创建REST API调用。 这不是REST的设计目的。 考虑以下内容:https://docs.djangoproject.com/ja/1.9/topics/http/middleware/

Django请求响应生命周期

正如你所看到的Django的请求/响应周期有相当多的开销。 加上webserver和wsgi容器的开销。 在客户端,你有与请求库相关的开销,但挂在秒,客户端也恰好是相同的Web应用程序,所以它也成为Web应用程序的开销的一部分。 而且存在着持久性问题(我将很快谈到)。

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

在没有其他API调用的情况下调用另一个视图非常简单

 other_app.other_view(request, **kwargs)

在这里已经讨论过几次这样的链接,例如基于另一个基于类的视图的基于Django Call Class的视图,并且我可以从另一个视图中调用视图吗? 所以我不会详细说明。

持续的请求

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

Session对象允许你在请求中保存某些参数。 它还会在Session实例所做的所有请求中保持cookie,并使用urllib3的连接池。 因此,如果您向同一主机发出多个请求,则会重新使用底层的TCP连接,这会导致性能显着提高

对你的Django视图的不同命中可能来自不同的用户,所以你不希望为内部REST调用重用相同的cookie。 另一个问题是python会话对象不能在django视图的两次命中之间持久化。 套接字通常不能被序列化,需要将它们夹持到memcached或redis中。

如果你仍然想坚持内部REST

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


如果你想避免将request传递给wrap_internal_api_call ,你需要做的只是在TemplateView的结尾做更多的工作,你可以在这里调用api包装器。 请注意,您的原始包装正在做很多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 ,但否则不会影响性能。 事实上,只需通过在api包装中执行cookies or request.COOKIES ,您就可以获得相同的轻微性能提升。

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


基本上我想摆脱那个请求参数(第一参数),因为然后调用它,我必须保持从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

有关设置中间件的信息,请参阅django文档

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!"
链接地址: http://www.djcxy.com/p/92633.html

上一篇: Passing session from template view to python requests api call

下一篇: Troubleshooting Azure App Service Push Notfication registration with tags in iOS