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

时间:2014-09-22 14:55:01

标签: django routing middleware

我正在尝试使用以下方案:

当GET请求进入我的“/”路由时,我通常想用我的HomeView处理它。但是,我的网站是重型AJAX所以如果请求的UserAgent是一个机器人,那么我用一个完全呈现的页面版本(标准的PhantomJS东西)来提供它。该方法工作正常,但完全呈现版本的性能和该版本的SLA与常规用户视图非常不同。因此,我想使用一块中间件来进行机器人检测,然后根据该中间件我将请求发送到另一个视图。

中间件部分很简单,我有一个process_request处理程序来检测机器人 - 没什么大不了的。但是,我无法想象覆盖将要调用的View函数的任何选项。在Django中有一种“正确”的方法吗?我目前的想法是:

  • 修改request.path_info以更改请求的URL,以便路由器再发送HtmlRendererView而不是HomeView
  • 直接从中间件调用HtmlRendererView并返回相应的HttpResponse。这感觉很笨重,因为它会剥夺任何其他中间件运行的机会。

注意:

  • 我不想返回重定向,抓取工具会获得相同资源的不同版本
  • 我在heroku上,所以在它击中Django之前我无法改写路线。如果我使用的是nginx,我可能只是将这个逻辑放在该层并在它到达Django之前重写URL。

2 个答案:

答案 0 :(得分:2)

这不是您问题的直接答案("将请求重新发送到其他视图"),但也许此解决方案可能会解决您的问题。

首先,保留中间件,但仅用它来检测访问者是否是机器人:

def process_request(self, request):     
    request.is_bot = is_bot(request) # assuming you have a function for detecting bots
    return 

然后创建一个基于类的视图,在request.is_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_bot方法,即可返回您对机器人的响应。

此解决方案的优点:

  • 您不需要为机器人编写不同的视图,只需创建专用方法
  • 您不会阻止其他中间件
  • 您的逻辑保留在您的视图中(而不是在您的中间件中)

虽然没有经过测试,但您可能需要调整代码。

编辑:

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

您可以使用中间件,并且仍然可以使所有中间件正常工作。您只需将自己的中间件放在MIDDLWARE_CLASSES中:

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_request用于检测机器人,process_view用于将机器人重定向到专用视图。

以下代码可能适用于您的情况:

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

答案 1 :(得分:0)

我目前的工作解决方案虽然没有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)

它具有从流中删除任何下游中间件的缺点,但它允许我在根视图路由到之前调用我的特定于爬虫的视图。