将模板视图中的会话传递给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的请求/响应周期有相当多的开销。 加上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