Django隐藏重定向



我想创建一种应用内重定向机制,它不会完全重定向,只是加载另一个视图函数。假设我在localhost:8000/a上,django渲染视图,视图的最后一行显示

return HttpResponseRedirect('/b')

所以现在我的浏览器会转到localhost:8000/b,django会渲染视图并将其返回给我

现在我只想在服务器端这样做。因此,当我转到localhost:8000/a,django决定重定向到/b时,它会为/b呈现视图,将其推送到所有中间件等中(就像重定向一样),并在根本没有任何重定向的情况下返回响应(如用户所见)。

我把这种重定向称为HttpResponseSmartRedirect,它看起来就像:

class HttpResponseSmartRedirect(HttpResponseRedirect):
    pass

现在我想为它创建中间件(作为所有中间件中的最后一个运行),它在process_response上检查响应是否是类HttpResponseSmartRedirect的实例。现在我的问题是,当我遇到这种情况时,如何使用url中的视图实际生成响应?通过使用urlsolvers中的resolve(),我可以很容易地获得视图函数,但我不知道如何强制它遍历所有中间件,并创建与我通过随意重定向获得的响应相同的响应。最糟糕的方法是只调用urllib2.get(url),但这句话的外观让我想毁掉我的电脑。

有什么想法可以让我在不叫丑陋的urllib的情况下做得很好吗?

您需要在Django请求处理代码的底部重新注入原始请求。

django.core.handlers.base中的BaseHandler.get_response开始处理请求。

因此,解决方案将类似于(未经测试!):

from django.core.handlers.base import BaseHandler
class SmartRedirectMiddleware(object):
    def process_response(self, request, response):
        if isinstance(response, HttpSmartRedirectResponse):
           handler = BaseHandler()
           request.url = response.redirect_url
           resp = handler.get_response(request)
           return resp

对于实际使用,您需要一些针对重定向循环和无限递归的保护。

或者你可以用不同的方法来解决这个问题——让浏览器根据重定向进行重新获取。如果这需要是不可见的,那么使用一些客户端Javascript就很容易实现。

BaseHandler示例(附加斜杠而不重定向,使用它代替CommonMiddleware,Django 2.1):

from django.http import HttpResponsePermanentRedirect, HttpRequest
from django.core.handlers.base import BaseHandler
from django.middleware.common import CommonMiddleware
from django.conf import settings

class HttpSmartRedirectResponse(HttpResponsePermanentRedirect):
    pass

class CommonMiddlewareAppendSlashWithoutRedirect(CommonMiddleware):
    """ This class converts HttpSmartRedirectResponse to the common response
        of Django view, without redirect.
    """
    response_redirect_class = HttpSmartRedirectResponse
    def __init__(self, *args, **kwargs):
        # create django request resolver
        self.handler = BaseHandler()
        # prevent recursive includes
        old = settings.MIDDLEWARE
        name = self.__module__ + '.' + self.__class__.__name__
        settings.MIDDLEWARE = [i for i in settings.MIDDLEWARE if i != name]
        self.handler.load_middleware()
        settings.MIDDLEWARE = old
        super(CommonMiddlewareAppendSlashWithoutRedirect, self).__init__(*args, **kwargs)
    def process_response(self, request, response):
        response = super(CommonMiddlewareAppendSlashWithoutRedirect, self).process_response(request, response)
        if isinstance(response, HttpSmartRedirectResponse):
            if not request.path.endswith('/'):
                request.path = request.path + '/'
            # we don't need query string in path_info because it's in request.GET already
            request.path_info = request.path
            response = self.handler.get_response(request)
        return response

最新更新