Django:如何使用中间件将请求重新路由到不同的视图
我尝试使用以下场景: 当GET请求进入我的“/”路由时,我通常希望使用HomeView处理它。然而,我的站点是重AJAX的,所以如果请求的UserAgent是一个bot,那么我就提供一个完全呈现的页面版本(标准的PhantomJS内容)。该方法工作得很好,但完全呈现版本的性能以及该版本的SLA与常规用户视图有很大不同。因此,我想使用一个中间件来进行bot检测,然后基于该中间件将请求发送到另一个视图 中间件部分很简单,我有一个进程请求处理程序来检测机器人-没什么大不了的。但是,我无法找到覆盖将被调用的视图函数的任何选项。在Django有没有一种“适当”的方法来做到这一点?我目前的想法是:Django:如何使用中间件将请求重新路由到不同的视图,django,routing,middleware,Django,Routing,Middleware,我尝试使用以下场景: 当GET请求进入我的“/”路由时,我通常希望使用HomeView处理它。然而,我的站点是重AJAX的,所以如果请求的UserAgent是一个bot,那么我就提供一个完全呈现的页面版本(标准的PhantomJS内容)。该方法工作得很好,但完全呈现版本的性能以及该版本的SLA与常规用户视图有很大不同。因此,我想使用一个中间件来进行bot检测,然后基于该中间件将请求发送到另一个视图 中间件部分很简单,我有一个进程请求处理程序来检测机器人-没什么大不了的。但是,我无法找到覆盖将被调
- 修改request.path_info以更改请求的URL,以便路由器随后发送HtmlRenderView而不是HomeView
- 直接从中间件调用HtmlRenderView并返回相应的HttpResponse。这让人感觉很笨拙,因为它会剥夺任何其他中间件运行的机会
- 我不想返回重定向,爬虫程序正在获取同一资源的不同版本
- 我在heroku上,所以在到达Django之前我不能重写路线。如果我使用的是nginx,我可能会把这个逻辑放在那一层,在它到达Django之前重写URL
def process_request(self, request):
request.is_bot = is_bot(request) # assuming you have a function for detecting bots
return
然后创建一个基于类的视图,在请求时调用特定方法。is\u bot
为True:
class BotViewMixin(object):
def dispatch(self, request, **kwargs):
if request.is_bot:
return self.handle_bot()
return super(BotViewMixin, self).dispatch(request, **kwargs)
然后,您可以在需要的任何位置继承此视图(例如,对于您的主页视图)。您只需在视图上创建一个handle\u bot
方法,该方法将返回您对bot的响应
此解决方案的优点:
- 您不需要为机器人编写不同的视图,只需创建一个专用的方法即可
- 你不会阻止其他的中间产品
- 您的逻辑保留在视图中(而不是中间件中)
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'yourproject.CrawlerDetector',
)
另外,我认为您应该编写两种中间件方法:process\u request
,用于检测机器人,以及将机器人重定向到专用视图
以下代码可能适用于您的情况:
from django.core.urlresolvers import reverse
class CrawlerDetector(object):
def process_request(self, request):
"""detect if the user agent is a bot"""
user_agent = request.META.get('HTTP_USER_AGENT', "")
request.is_bot = self.is_crawler(user_agent)
return
def process_view(request, view_func, view_args, view_kwargs):
if request.is_bot and request.path == reverse('home_page'):
return HtmlRendererView().get(request)
return
我当前的工作解决方案虽然没有Eliot建议的解决方案那么干净,但看起来(基本上)是这样的:
class CrawlerDetector(object):
# Middleware that detects requests that should be rendered by the HtmlRendererView.
def process_request(self, request):
user_agent = request.META.get('HTTP_USER_AGENT', "")
if not self.is_crawler(user_agent):
return None
return HtmlRendererView().get(request)
它的缺点是从流中删除任何下游中间件,但它确实允许我在根视图路由到之前调用特定于爬虫的视图。谢谢您的回复,@eliotberrio。我认为您概述的方法的唯一问题是,我进行此更改的主要动机是为了让NewRelic能够提供有关我的BotView与我的HomeView的准确统计数据。所以我的设计灵活性有点有限。如果我使用了dispatch方法,那么我相信Django已经选择了我的视图,NewRelic将把调用属性化为该视图。好的,那么你必须为你的机器人返回另一个视图。我将编辑我的答案。更新:更改路径信息无效。