子类化Django UpdateCacheMiddleware和FetchFromCacheMiddleware的技术

Ben*_*Ben 7 django caching middleware django-views

我已经使用UpdateCacheMiddlewareFetchFromCacheMiddlewareMiddleWare来实现站点范围的匿名缓存,以获得不同程度的成功.

最大的问题是中间件只缓存匿名用户的第一个请求.由于在第一个响应上设置了session_id cookie,因此匿名用户的后续请求不会因为Headers上的视图级缓存变化而到达缓存.

我的网页在匿名用户中没有显着差异,并且只要它们有所不同,我可以通过Ajax处理.结果,我决定尝试将Django的缓存中间件子类化为不再改变Header.相反,它在Anonymous vs. LoggedIn Users上有所不同.因为我正在使用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)
Run Code Online (Sandbox Code Playgroud)

负责从缓存中检索页面的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)
Run Code Online (Sandbox Code Playgroud)

UpdateCacheMiddleware显然,有很多复制品.我无法想出一个更好的钩子来使这个更干净.

这通常看起来是一个好方法吗?想到任何明显的问题?

谢谢,本

okm*_*okm 2

您可以通过暂时删除不需要的变化字段来解决此问题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
Run Code Online (Sandbox Code Playgroud)

另外,CACHE_MIDDLEWARE_ANONYMOUS_ONLY = True在设置中进行设置以防止对经过身份验证的用户进行缓存。