我想创建一种应用内重定向机制,它不会完全重定向,只是加载另一个视图函数。假设我在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