为什么Django和CherryPy不原生支持基于HTTP方法的调度?

12 投票
4 回答
2370 浏览
提问于 2025-04-15 13:30

向一个网址发送POST请求和发送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'])

我在网上找到的为了解决这个问题的方法(比如这个代码片段用于基于请求类型的处理,或者这个装饰器用于请求类型的要求)都不是很优雅,因为它们显然只是一些变通办法。

CherryPy的情况似乎也是这样。我知道的只有web.py和Google App Engine的框架处理得比较好。

我认为这是一个严重的设计缺陷,影响了这个网络框架的使用。有人同意这个看法吗?还是说这是基于我不知道的原因或需求而做出的故意选择?

4 个回答

2

我觉得选择使用Django的原因是,通常只需要用到GETPOST这两种请求方式,这样可以让框架的使用变得更简单。这样一来,开发者就不用太在意使用了哪种请求方式了。

不过,还有很多其他的框架可以根据请求方式来处理请求。我个人喜欢werkzeug,它让你可以轻松定义自己的处理代码,这样你就可以根据自己的需求来处理请求,灵活性很高。

6

在网上看到这个内容,觉得有必要更新一下。

Django

顺便说一下,现在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() 部分负责处理这些请求 -

dispatch(request, *args, **kwargs)

这是视图的核心部分 – 它接受一个请求参数和其他参数,并返回一个HTTP响应。

默认情况下,它会检查HTTP方法,并尝试调用与该方法匹配的方法;比如,GET请求会调用 get(),POST请求会调用 post(),依此类推。

默认情况下,HEAD请求会被当作GET请求来处理。如果你想以不同的方式处理HEAD请求,可以重写 head() 方法。想了解更多,可以查看支持其他HTTP方法的例子。

默认实现还会将请求、参数和关键字参数设置为实例变量,这样视图中的任何方法都能获取到调用视图时的完整请求信息。

然后你可以在 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'),
)

更多细节请查看这里

CherryPy

CherryPy现在也支持这个功能。他们有一个完整的页面来介绍这个内容。

13

我不能代表Django说话,但在CherryPy中,你可以为每种HTTP请求方式(比如GET、POST等)设置一个函数,只需要一个配置项:

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并不会替你做决定,但如果你想这样做,它会让这件事变得简单(只需要一行代码)。

撰写回答