为什么Django和CherryPy不原生支持基于HTTP方法的调度?
向一个网址发送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 个回答
我觉得选择使用Django的原因是,通常只需要用到GET
和POST
这两种请求方式,这样可以让框架的使用变得更简单。这样一来,开发者就不用太在意使用了哪种请求方式了。
不过,还有很多其他的框架可以根据请求方式来处理请求。我个人喜欢werkzeug,它让你可以轻松定义自己的处理代码,这样你就可以根据自己的需求来处理请求,灵活性很高。
在网上看到这个内容,觉得有必要更新一下。
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现在也支持这个功能。他们有一个完整的页面来介绍这个内容。
我不能代表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并不会替你做决定,但如果你想这样做,它会让这件事变得简单(只需要一行代码)。