2014-09-22 57 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) 

它從流中刪除任何下游中間件都有缺點,但它允許我在根視圖路由到之前調用特定於搜索器的視圖。