【问题标题】:Why don't Django and CherryPy support HTTP verb-based dispatch natively?为什么 Django 和 CherryPy 本身不支持基于 HTTP 动词的调度?
【发布时间】:2025-12-27 07:30:17
【问题描述】:

POST 到 URL 与 GET、DELETE 或 PUT 不同。这些行动是根本不同的。然而,Django 似乎在其调度机制中忽略了它们。基本上,人们要么完全忽略 HTTP 动词,要么在每个视图上都这样做:

def my_view(request, arg1, arg2):
    if request.method == 'GET':
        return get_view(request, arg1, arg2)
    if request.method == 'POST':
        return post_view(request, arg1, arg2)
    return http.HttpResponseNotAllowed(['GET', 'POST'])

我在网络上找到的几个解决方案(this snippet 用于基于动词的调度,或this decorator 用于动词要求)不是很优雅,因为它们显然只是解决方法。

CherryPy 的情况似乎是一样的。我所知道的唯一能做到这一点的框架是 web.py 和 Google App Engine 的。

我认为这是 Web 框架的严重设计缺陷。有人同意吗?还是基于我忽略的原因/要求而深思熟虑的决定?

【问题讨论】:

    标签: python django rest cherrypy web-frameworks


    【解决方案1】:

    我不能代表 Django,但在 CherryPy 中,每个 HTTP 动词可以有一个函数,只需一个配置条目:

    request.dispatch = cherrypy.dispatch.MethodDispatcher()
    

    但是,我已经看到了一些不可取的情况。

    一个例子是不考虑动词的硬重定向。

    另一种情况是您的大多数处理程序只处理 GET。在这种情况下,有一千个页面处理程序都命名为“GET”,这尤其令人讨厌。在装饰器中表达这一点比在函数名中更漂亮:

    def allow(*methods):
        methods = list(methods)
        if not methods:
            methods = ['GET', 'HEAD']
        elif 'GET' in methods and 'HEAD' not in methods:
            methods.append('HEAD')
        def wrap(f):
            def inner(*args, **kwargs):
                cherrypy.response.headers['Allow'] = ', '.join(methods)
                if cherrypy.request.method not in methods:
                    raise cherrypy.HTTPError(405)
                return f(*args, **kwargs):
            inner.exposed = True
            return inner
        return wrap
    
    class Root:
        @allow()
        def index(self):
            return "Hello"
    
        cowboy_greeting = "Howdy"
    
        @allow()
        def cowboy(self):
            return self.cowboy_greeting
    
        @allow('PUT')
        def cowboyup(self, new_greeting=None):
            self.cowboy_greeting = new_greeting
    

    我看到的另一个常见问题是在数据库中查找与资源对应的数据,无论动词如何,这都应该发生:

    def default(self, id, **kwargs):
        # 404 if no such beast
        thing = Things.get(id=id)
        if thing is None:
            raise cherrypy.NotFound()
    
        # ...and now switch on method
        if cherrypy.request.method == 'GET': ...
    

    CherryPy 试图不为您做出决定,但如果您想要的话,它会使其变得容易(单行)。

    【讨论】:

      【解决方案2】:

      从 Google 看到这个,并考虑更新。

      姜戈

      仅供参考,现在 Django 支持作为基于类的视图。您可以扩展泛型类View 并添加get()post()put() 等方法。 -

      from django.http import HttpResponse
      from django.views.generic import View
      
      class MyView(View):
      
          def get(self, request, *args, **kwargs):
              return HttpResponse('Hello, World!')
      

      dispatch() 部分处理这个 -

      调度(请求,*args,**kwargs)

      视图的视图部分—— 接受请求参数和参数的方法,并返回一个 HTTP 响应。

      默认实现将检查 HTTP 方法并尝试 委托给与 HTTP 方法匹配的方法;一个 GET 将是 委托给 get(),一个 POST 给 post(),等等。

      默认情况下,HEAD 请求将委托给 get()。如果你需要 以不同于 GET 的方式处理 HEAD 请求,您可以覆盖 头()方法。有关示例,请参阅支持其他 HTTP 方法。

      默认实现还将 request、args 和 kwargs 设置为 实例变量,因此视图上的任何方法都可以知道完整的 为调用视图而发出的请求的详细信息。

      那你就可以在urls.py使用了——

      from django.conf.urls import patterns, url
      
      from myapp.views import MyView
      
      urlpatterns = patterns('',
          url(r'^mine/$', MyView.as_view(), name='my-view'),
      )
      

      More details.

      CherryPy

      CherryPy 现在也支持这一点。他们对此有一个full page

      【讨论】:

        【解决方案3】:

        我相信 django 的决定是因为通常只需 GETPOST 就足够了,这使得框架更简单地满足其要求。只“不在乎”使用哪个动词非常方便。

        但是,还有很多其他框架可以基于动词进行调度。我喜欢werkzeug,它可以很容易地定义你自己的调度代码,所以你可以根据你想要的任何东西来调度。

        【讨论】:

        • 尽管通常 GET 和 POST 就足够了,但动词的数量不是这里的问题。我只是想不出一个用例,其中控制器(在 Django 中称为“视图”)应该对 GET 和 POST 执行完全相同的操作。
        • GET 和 POST 很常见,我同意。但是,我仍然不明白为什么默认应该是完全忽略动词。
        • @martin:为什么不呢?他们差不多。您可以轻松编写一个不关心请求是作为 get 还是作为 post 来的应用程序。
        • @nosklo:因为它们根本不是一回事。可以安全忽略动词的情况非常奇怪(我仍然没有看到一个)。因此,这些框架使最不常见的情况(动词无关紧要)成为默认值,而最常见的情况(动词很重要)成为可选的、非标准化的功能。我认为应该反过来。
        • 忽略动词会使视图函数变得臃肿且充满if POST, if GET
        【解决方案4】:

        因为这并不难DIY。只需要一个字典,其中包含每个类中函数的公认动词。

        def dispatcher(someObject, request):
            try:
              return someObject.acceptedVerbs[request.method]()
            except:
              return http.HttpResponseNotAllowed(someObject.acceptedVerbs.keys())
        

        【讨论】:

        • 这个看起来不错。有关如何将该调度程序连接到一个简单的 Django 应用程序的任何信息?
        • 我有一个实用程序文件,我包含在我更具体的视图文件中。每个特定文件在调用此调度程序函数时都知道要使用哪个对象。
        • 抱歉,我还是没看清楚。将 url 模式连接到特定于动词的处理程序需要多少代码(除了您的 dispatcher 函数)?可以举个例子吗?
        • import my_views_utils.py 后跟 dispatcher(someHandlerObjectOfYours, request) 在您在 urls.py 中定义的处理程序函数中。它不是 Rails,但它也同样有效。