Django:如何使用中间件将请求重新路由到不同的视图

Django:如何使用中间件将请求重新路由到不同的视图,django,routing,middleware,Django,Routing,Middleware,我尝试使用以下场景: 当GET请求进入我的“/”路由时,我通常希望使用HomeView处理它。然而,我的站点是重AJAX的,所以如果请求的UserAgent是一个bot,那么我就提供一个完全呈现的页面版本(标准的PhantomJS内容)。该方法工作得很好,但完全呈现版本的性能以及该版本的SLA与常规用户视图有很大不同。因此,我想使用一个中间件来进行bot检测,然后基于该中间件将请求发送到另一个视图 中间件部分很简单,我有一个进程请求处理程序来检测机器人-没什么大不了的。但是,我无法找到覆盖将被调

我尝试使用以下场景:

当GET请求进入我的“/”路由时,我通常希望使用HomeView处理它。然而,我的站点是重AJAX的,所以如果请求的UserAgent是一个bot,那么我就提供一个完全呈现的页面版本(标准的PhantomJS内容)。该方法工作得很好,但完全呈现版本的性能以及该版本的SLA与常规用户视图有很大不同。因此,我想使用一个中间件来进行bot检测,然后基于该中间件将请求发送到另一个视图

中间件部分很简单,我有一个进程请求处理程序来检测机器人-没什么大不了的。但是,我无法找到覆盖将被调用的视图函数的任何选项。在Django有没有一种“适当”的方法来做到这一点?我目前的想法是:

  • 修改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的响应

此解决方案的优点:

  • 您不需要为机器人编写不同的视图,只需创建一个专用的方法即可
  • 你不会阻止其他的中间产品
  • 您的逻辑保留在视图中(而不是中间件中)
但这并没有经过测试,所以您可能需要修改代码

编辑:

由于您使用NewRelic,并且必须使用专用的机器人视图才能获得准确的统计数据,因此这种方法不适用于您

您可以使用中间件,但仍然可以让所有中间件正常工作。您只需将自己的中间件放在MIDDLWARE_类的最后:

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将把调用属性化为该视图。好的,那么你必须为你的机器人返回另一个视图。我将编辑我的答案。更新:更改路径信息无效。