7

私はミドルウェアUpdateCacheMiddlewareFetchFromCacheMiddlewareミドルウェアを使用して、サイト全体の匿名キャッシングをさまざまなレベルで有効にしました。

最大の問題は、ミドルウェアが匿名ユーザーの最初の要求のみをキャッシュすることです。その最初の応答に session_id Cookie が設定されるため、その匿名ユーザーによる後続の要求は、ビュー レベルのキャッシュがヘッダーで変化するため、キャッシュにヒットしません。

私の Web ページは、匿名ユーザー間で意味のある違いはありません。違いがある限り、Ajax を介して処理できます。その結果、Django のキャッシング ミドルウェアをサブクラス化して、Header がオンにならないようにすることにしました。代わりに、匿名ユーザーとログイン ユーザーによって異なります。私は Auth バックエンドを使用しており、そのハンドラーはキャッシュからフェッチする前に発生するため、機能しているようです。

class AnonymousUpdateCacheMiddleware(UpdateCacheMiddleware):

    def process_response(self, request, response):
        """
        Sets the cache, if needed.
        We are overriding it in order to change the behavior of learn_cache_key().
        """

        if not self._should_update_cache(request, response):
            # We don't need to update the cache, just return.
            return response
        if not response.status_code == 200:
            return response

        timeout = get_max_age(response)
        if timeout == None:
            timeout = self.cache_timeout
        elif timeout == 0:
            # max-age was set to 0, don't bother caching.
            return response
        patch_response_headers(response, timeout)
        if timeout:
            ######### HERE IS WHERE IT REALLY GOES DOWN #######
            cache_key = self.learn_cache_key(request, response, self.cache_timeout, self.key_prefix, cache=self.cache)
            if hasattr(response, 'render') and callable(response.render):
                response.add_post_render_callback(
                    lambda r: self.cache.set(cache_key, r, timeout)
                )
            else:
                self.cache.set(cache_key, response, timeout)
        return response

    def learn_cache_key(self, request, response, timeout, key_prefix, cache=None):
        """_generate_cache_header_key() creates a key for the given request path, adjusted for locales.

            With this key, a new cache key is set via _generate_cache_key() for the HttpResponse

            The subsequent anonymous request to this path hits the FetchFromCacheMiddleware in the
            request capturing phase, which then looks up the headerlist value cached here on the initial response.

            FetchFromMiddleWare calcuates a cache_key based on the values of the listed headers using _generate_cache_key
            and then looks for the response stored under that key.  If the headers are the same as those
            set here, there will be a cache hit and the cached HTTPResponse is returned.
        """

        key_prefix = key_prefix or settings.CACHE_MIDDLEWARE_KEY_PREFIX
        cache_timeout = self.cache_timeout or settings.CACHE_MIDDLEWARE_SECONDS
        cache = cache or get_cache(settings.CACHE_MIDDLEWARE_ALIAS)

        cache_key = _generate_cache_header_key(key_prefix, request)

        # Django normally varies caching by headers so that authed/anonymous users do not see same pages
        # This makes Google Analytics cookies break caching;
        # It also means that different anonymous session_ids break caching, so only first anon request works
        # In this subclass, we are ignoring headers and instead varying on authed vs. anonymous users
        # Alternatively, we could also strip cookies potentially for the same outcome

        # if response.has_header('Vary'):
        #     headerlist = ['HTTP_' + header.upper().replace('-', '_')
        #                   for header in cc_delim_re.split(response['Vary'])]
        # else:
        headerlist = []

        cache.set(cache_key, headerlist, cache_timeout)
        return _generate_cache_key(request, request.method, headerlist, key_prefix)

キャッシュからページを取得する Fetcher は次のようになります。

class AnonymousFetchFromCacheMiddleware(FetchFromCacheMiddleware):

    def process_request(self, request):
        """
        Checks whether the page is already cached and returns the cached
        version if available.
        """
        if request.user.is_authenticated():
            request._cache_update_cache = False
            return None
        else:
            return super(SmarterFetchFromCacheMiddleware, self).process_request(request)

UpdateCacheMiddleware明らかに、のコピーがたくさんありました。これをよりきれいにするためのより良いフックを見つけることができませんでした。

これは一般的に良いアプローチのように思えますか? 頭に浮かぶ明らかな問題はありますか?

ありがとう、ベン

4

1 に答える 1

2

から不要な vary フィールドを一時的に削除することで、この問題を回避できますresponse['Vary']

from django.utils.cache import cc_delim_re

class AnonymousUpdateCacheMiddleware(UpdateCacheMiddleware):
    def process_response(self, request, response):
        vary = None
        if not request.user.is_authenticated() and response.has_header('Vary'):
                vary = response['Vary']
                # only hide cookie here, add more as your usage
                response['Vary'] = ', '.join(
                    filter(lambda v: v != 'cookie', cc_delim_re.split(vary))
        response = super(AnonymousUpdateCacheMiddleware, self).process_response(request, response)
        if vary is not None:
            response['Vary'] = vary
        return response

また、CACHE_MIDDLEWARE_ANONYMOUS_ONLY = True認証されたユーザーのキャッシュを防止するように設定してください。

于 2012-06-16T06:15:09.297 に答える