首先引用django/middleware/common.py中的简介
"""
"Common" middleware for taking care of some basic operations:
- Forbid access to User-Agents in settings.DISALLOWED_USER_AGENTS
- URL rewriting: Based on the APPEND_SLASH and PREPEND_WWW settings,
append missing slashes and/or prepends missing "www."s.
- If APPEND_SLASH is set and the initial URL doesn't end with a
slash, and it is not found in urlpatterns, form a new URL by
appending a slash at the end. If this new URL is found in
urlpatterns, return an HTTP redirect to this new URL; otherwise
process the initial URL as usual.
This behavior can be customized by subclassing CommonMiddleware and
overriding the response_redirect_class attribute.
"""
主要内容就是:
如果设置了APPEND_SLASH,并且初始的url没有以斜杠结尾,在urlpatterns中找不到对应的路由与之匹配,这个common的中间件就好通过在结尾加斜线。这样在拼接了 "/" 的链接中找到此对应的urlpatterns,返回指向此新url的http重定向。
在rest_framework/routers.py中的107、119、126、140行,我们可以看到
url=r'^{prefix}{trailing_slash}$'
url=r'^{prefix}/{url_path}{trailing_slash}$'
url=r'^{prefix}/{lookup}{trailing_slash}$'
url=r'^{prefix}/{lookup}/{url_path}{trailing_slash}$'
都是以 trailing_slash加上$符结尾。
就是不论你是list detail 还是自定义的方法 都会走这个路由匹配。
APPEND_SLASH 是默认为True
也就是按common中间件处理:
比如 我们用了一个ModelViewSet 路由地址是 127.0.0.1:8000/zoo/
我们GET 请求 127.0.0.1:8000/zoo 但是没有匹配到^{prefix}/zoo{trailing_slash}$ 因为没有后面的 / ,由于APPEND_SLASH 为True,所以地址拼接了斜杠 就变成了 127.0.0.1:8000/zoo/ 然后路由匹配到,返回列表接口
我们GET 请求 127.0.0.1:8000/zoo/1 但是没有匹配到 ^{prefix}/zoo/(?P<pk>[^/.]+){trailing_slash}$ 因为没有后面的 / ,由于APPEND_SLASH 为True,所以地址拼接了斜杠 就变成了 127.0.0.1:8000/zoo/1/ 然后路由匹配到,返回详情接口
这样看来,通常情况下是没有问题的。
但是如果我们使用的方法不是GET请求 而是 POST、DELETE、PATCH、PUT呢?
直接报错
RuntimeError at /api/musiclibrary/1
You called this URL via PATCH, but the URL doesn't end in a slash and you have APPEND_SLASH set.
Django can't redirect to the slash URL while maintaining PATCH data.
Change your form to point to 127.0.0.1:8000/zoo/1/ (note the trailing slash),
or set APPEND_SLASH=False in your Django settings.
他让我们在链接后面加上 "/" 或者 将APPEND_SLASH改为False
但是改为False一样不起作用啊,匹配不到路由啊
按理说加他这个自动追加斜杠的方法是没问题啊,为什么除了GET方法就不行了呢?
关键就在于前面中间件里面的这句话 return an HTTP redirect to this new URL
重定向啊,只支持GET方法 所以失败了。
from rest_framework.routers import SimpleRouter
class StandardRouter(SimpleRouter):
def __init__(self, trailing_slash='/?'):
super(StandardRouter, self).__init__()
self.trailing_slash = trailing_slash
我们自定义一个路由,继承SimpleRouter ,初始化之后修改他的 trailing_slash 为 /?
这样不就是有斜杠和没有斜杠都可以吗 多简单
然后在你对应的app下的urls下写:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from common.routers import StandardRouter
from zoo.views import ZooViewSet
router = StandardRouter()
router.register(r'zoo', ZooViewSet, basename='zoo')
urlpatterns = router.urls