当URL中没有尾随斜杠时,使用VueJS单页的Django路由具有意外行为

2024-10-08 23:19:31 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个Django后端,VueJS前端组合,我通过Django提供restapi,还有一个带有VueJS和vue路由器的单页应用程序。你知道吗

this问题中,我得到了在我的main中使用以下url的技巧网址.py地址:

urlpatterns = [
    re_path(r'^(?P<filename>(robots.txt)|(humans.txt))$', views.home_files, name='home-files'),
    path('api/', include('backend.urls', namespace='api')),
    path('auth/', include('auth.urls')),
    path('admin/', admin.site.urls),
    re_path(r'^.*$', views.vue), # VueJS frontend
]

所以我希望URL的行为如下:

{baseDomain}/api/users/1/ -> go to backend.urls  
{baseDomain}/auth/login/ -> go to auth.urls  
{baseDomain}/admin/ -> go to admin page  
{baseDomain}/de/home -> vue-router takes over

现在这些url工作得非常好,但是我希望{baseDomain}/api/users/1(没有尾部斜杠)仍然可以使用backend.url,但是发生的事情是我在Vue页面上着陆。你知道吗

在中添加APPEND\u SLASH=True设置.py也没有帮助,因为它只在找不到要加载的页面时附加斜杠。但由于我的前端正则表达式匹配任何东西,它总是重定向到Vue。你知道吗

我试图通过添加以下内容来修复它:

re_path(r'.*(?<!/)$', views.redirect_with_slash)

代码如下:

def redirect_with_slash(request):
    '''Redirects a requested url with a slash at the end'''
    if request.path == '/':
        return render(request, 'frontend/index.html')
    return redirect(request.path + '/')

但它不是很优雅。还要注意if request.path == '/':。奇怪的是,Django会将“/”与regex r'.*(?<!/)$'匹配,然后重定向到“/”,这是一个无效的URL并显示一个错误页,所以我不得不包含这个if语句。你知道吗

有人有解决办法吗?在引用的问题中,这似乎不是一个问题,所以我想知道为什么它出现在我的项目中。你知道吗

编辑:后端网址.py你知道吗

"""
backend urls.py
"""
from django.urls import include, path
from rest_framework_nested import routers
from auth.views import UserViewSet, GroupViewSet, ProjectViewSet
from .views import IfcViewSet, IfcFileViewSet

app_name = 'api'

router = routers.DefaultRouter() #pylint: disable=C0103
router.register(r'users', UserViewSet)
router.register(r'groups', GroupViewSet)
router.register(r'projects', ProjectViewSet)
projects_router = routers.NestedSimpleRouter(router, r'projects', lookup='project')
projects_router.register(r'models', IfcFileViewSet, base_name='projects-models')

urlpatterns = [
    path('', include(router.urls)),
    path('', include(projects_router.urls))
]

"""
auth urls.py
"""
from django.urls import path, include
from rest_framework import routers
from rest_framework_jwt.views import obtain_jwt_token, refresh_jwt_token
from .views import RegistrationViewSet

app_name = 'authentication'
router = routers.DefaultRouter()
router.register('register', RegistrationViewSet)

urlpatterns = [
    path('', include(router.urls)),
    path('', include('rest_auth.urls')),
    path('refresh_token/', refresh_jwt_token),
]

Tags: pathfrompyimportauthregisterapiinclude
1条回答
网友
1楼 · 发布于 2024-10-08 23:19:31

问题是您在re_path(r'^.*$', views.vue)中有一个catch all,因此如果任何URL在前面的path上不完全匹配,这将被触发。你知道吗

Django的CommonMiddleware实际上附加了一个尾部斜杠和重定向,当它找到一个404并且URL路径不是以/结束时(取决于APPEND_SLASH设置),但是这取决于响应。你知道吗

在您的例子中,如果请求路径不是以/结尾,您可以有一个微小的请求中间件,它会在后面加上斜杠,例如:

from django.shortcuts import redirect

class AppendTrailingSlashOnRequestMiddleware:

    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):

        if not request.path.endswith('/'):
            query_string = request.META['QUERY_STRING']
            query_string = f'?{query_string}' if query_string else ''
            to_url = f'{request.path}/{query_string}'
            return redirect(to_url, permanent=True)

        response = self.get_response(request)

        return response

将中间件添加到settings.MIDDLEWARE显然,最好将其放在顶部,以防止来自其他中间件的不必要的处理,因为我们将以任何方式重定向,然后也需要处理。你知道吗


但这有一个问题:在执行重定向时,POST/PUT/PATCH中的数据将丢失(这里我们执行301,但同样适用于302)。有Temporary Redirect 307可以在这方面帮助我们,好的事情是所有的规则 包括IE在内的浏览器都支持这一点。但Django没有现成的解决方案,因此我们需要自己实施:

from django.http.response import HttpResponseRedirectBase

class HttpTemporaryResponseRedirect(HttpResponseRedirectBase):
    status_code = 307

现在,在中间件中导入它,并使用它来代替redirect

class AppendTrailingSlashOnRequestMiddleware:

    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):

        if not request.path.endswith('/'):
            query_string = request.META['QUERY_STRING']
            query_string = f'?{query_string}' if query_string else ''
            to_url = f'{request.path}/{query_string}'
            return HttpTemporaryResponseRedirect(to_url)  # here

        response = self.get_response(request)

        return response

注意:如果您想保留GET的浏览器缓存功能,可以基于request.method重定向到301/307。你知道吗

相关问题 更多 >

    热门问题