2014-09-22 38 views
2

我想有以下情形的工作重新路由到不同的视图的请求:Django的:如何使用中间件

当GET请求进入我的“/”的路线,我通常要处理它与我的HomeView。但是,我的网站是沉重的AJAX,所以如果请求的UserAgent是一个机器人,那么我就可以使用该页面的完全渲染版本(标准PhantomJS东西)提供它。该方法可以正常工作,但完全呈现版本的性能以及该版本的SLA与普通用户视图有很大不同。因此,我想用一块中间件来执行机器人检测,然后基于该中间件,我想将请求发送到不同的视图。

中间件部分很简单,我有一个process_request处理程序来检测bot - 没什么大不了的。但是,我无法找到重写将被调用的View函数的任何选项。在Django中有没有“适当”的方法?我现在的想法是:

  • 修改request.path_info变更请求的URL,这样路由器将随后从中间件发送HtmlRendererView而不是HomeView
  • 调用HtmlRendererView直接返回相应的HttpResponse。这让人觉得笨拙,因为它会剥夺其他中间件运行的机会。

注:

  • 我不想返回重定向,履带越来越不同版本的同一资源
  • 我在Heroku所以我不能在它碰到Django之前重写路由。如果我使用的是nginx,那么我可能只是把这个逻辑放在那个层上,并在它打到Django之前重写URL。
+0

更新:改变path_info不起作用。 – 2014-09-22 16:39:22

回答

2

这不是直接回答你的问题(“重新请求不同的视图”),但也许这个解决方案可以解决你的问题。

首先,你把你的中间件,但只使用它来检测,如果访问者是一个机器人:

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

然后创建调用特定的方法时request.is_bot是真正的一类基于视图:

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 
+0

感谢您的回复,@EliotBerriot。 我认为你提到的方法唯一的问题是我做这个改变的主要动机是让NewRelic将提供我的BotView和我的HomeView的精确统计数据。所以我的设计灵活性有限。如果我使用调度方法,那么我相信Django已经选择了我的View和NewRelic将调用该视图。 – 2014-09-22 16:33:53

+0

好的,所以你必须为你的机器人返回另一个视图。我会编辑我的答案。 – 2014-09-23 07:19:11

0

我目前工作的解决方案,而不是那样干净艾略特的建议的解决方案,外观(基本上)是这样的:

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) 

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