我想使用请求库从我的Django TemplateView进行多个内部REST API调用。现在我想将会话也从模板视图传递到api调用。建议的方法是什么,牢记性能。
现在,我在模板视图中从当前cookie
对象中提取request
,并将其传递给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传递到内部服务。另外,如何在多个呼叫之间保持持久连接?</ p>
答案 0 :(得分:4)
虽然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 based view from another class based view和Can I call a view from within another view?这样的链接上进行了几次讨论,所以我不会详细说明。
持久性http请求(谈论python请求而非django.http.request.HttpRequest)通过会话对象进行管理(同样不要与django会话混淆)。避免混淆真的很难:
Session对象允许您跨越某些参数 要求。它还会在所有请求中保留cookie 会话实例,并将使用urllib3的连接池。因此,如果 您正在向同一主机(底层TCP)发出多个请求 连接将被重用,这可能会导致重大连接 绩效提升
您的django视图的不同点击可能来自不同的用户,因此您不希望将相同的Cookie重用于内部REST调用。另一个问题是python会话对象不能在两个不同的django视图之间保持持久化。套接字通常不能被序列化,这是将它们放入memcached或redis的要求。
我认为@julian的答案显示了如何避免将django请求实例作为参数传递。
答案 1 :(得分:1)
如果你想避免将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
,但不会对性能产生影响。实际上,只需在api包装器中执行cookies or request.COOKIES
一次内部即可获得相同的轻微性能提升。
网络始终是任何应用程序中最严重的瓶颈。因此,如果这些内部API与TemplateView在同一台机器上,那么性能的最佳选择是避免进行API调用。
答案 2 :(得分:-1)
基本上我想摆脱那个请求参数(第一个参数),因为之后要调用它我不断将请求对象从TemplateViews传递到内部服务。
要传递函数args而不将它们显式传递给函数调用,您可以使用装饰器来包装函数并自动注入参数。使用全局变量和一些django中间件在它到达您的视图之前注册请求将解决您的问题。请参阅下文,了解我的意思的抽象和简化版本。
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
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
from request_decorators import request_injector
@request_injector
def internal_function(request):
return request
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!"