为什么 Django 和 CherryPy 本身不支持基于 HTTP 动词的调度?

发布于 2024-07-30 04:02:22 字数 799 浏览 10 评论 0原文

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'])

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

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

我认为这是 Web 框架的一个严重的设计缺陷。 有人同意吗? 或者这是基于我忽略的原因/要求而故意做出的决定?

It's not the same to POST to an URL than to GET it, DELETE it or PUT it. These actions are fundamentally different. However, Django seems to ignore them in its dispatch mechanism. Basically, one is forced to either ignore HTTP verbs completely or do this on every view:

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'])

The few solutions I have found for this in the web (this snippet for verb-based dispatch, or this decorator for verb requirement) are not very elegant as they are clearly just workarounds.

The situation with CherryPy seems to be the same. The only frameworks I know of that get this right are web.py and Google App Engine's.

I see this as a serious design flaw for a web framework. Does anyone agree? Or is it a deliberate decision based on reasons/requirements I ignore?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

冧九 2024-08-06 04:02:22

我不能代表 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 尝试不为您做出决定,但会做出决定如果你想要的话,这很容易(一句台词)。

I can't speak for Django, but in CherryPy, you can have one function per HTTP verb with a single config entry:

request.dispatch = cherrypy.dispatch.MethodDispatcher()

However, I have seen some situations where that's not desirable.

One example would be a hard redirect regardless of verb.

Another case is when the majority of your handlers only handle GET. It's especially annoying in that case to have a thousand page handlers all named 'GET'. It's prettier to express that in a decorator than in a function name:

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

Another common one I see is looking up data corresponding to the resource in a database, which should happen regardless of verb:

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 tries to not make the decision for you, yet makes it easy (a one-liner) if that's what you want.

橘和柠 2024-08-06 04:02:22

从谷歌上看到这个,并想到更新。

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() 部分处理这个 -

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

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

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

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

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

然后你可以在 urls.py 中使用它 -

from django.conf.urls import patterns, url

from myapp.views import MyView

urlpatterns = patterns('',
    url(r'^mine/

更多详细信息

CherryPy

CherryPy 现在也支持这一点。 他们对此有一个完整页面

, MyView.as_view(), name='my-view'), )

更多详细信息

CherryPy

CherryPy 现在也支持这一点。 他们对此有一个完整页面

Came across this from Google, and thought of updating.

Django

Just FYI, This is now supported in Django as class based views. You can extend the generic class View and add methods like get(), post(), put() etc. E.g. -

from django.http import HttpResponse
from django.views.generic import View

class MyView(View):

    def get(self, request, *args, **kwargs):
        return HttpResponse('Hello, World!')

The dispatch() part handles this-

dispatch(request, *args, **kwargs)

The view part of the view – the
method that accepts a request argument plus arguments, and returns a
HTTP response.

The default implementation will inspect the HTTP method and attempt to
delegate to a method that matches the HTTP method; a GET will be
delegated to get(), a POST to post(), and so on.

By default, a HEAD request will be delegated to get(). If you need to
handle HEAD requests in a different way than GET, you can override the
head() method. See Supporting other HTTP methods for an example.

The default implementation also sets request, args and kwargs as
instance variables, so any method on the view can know the full
details of the request that was made to invoke the view.

Then you can use it in urls.py -

from django.conf.urls import patterns, url

from myapp.views import MyView

urlpatterns = patterns('',
    url(r'^mine/

More details.

CherryPy

CherryPy now also supports this. They have a full page on this.

, MyView.as_view(), name='my-view'), )

More details.

CherryPy

CherryPy now also supports this. They have a full page on this.

软糯酥胸 2024-08-06 04:02:22

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

然而,还有很多其他框架可以基于动词进行调度。 我喜欢 werkzeug,它可以轻松定义自己的调度代码,这样你就可以基于调度随心所欲,随心所欲。

I believe the decision for django was made because usually just GET and POST is enough, and that keeps the framework simpler for its requirements. It is very convenient to just "not care" about which verb was used.

However, there are plenty other frameworks that can do dispatch based on verb. I like werkzeug, it makes easy to define your own dispatch code, so you can dispatch based on whatever you want, to whatever you want.

牵强ㄟ 2024-08-06 04:02:22

因为这个DIY并不难。 只要有一本字典,其中包含每个类别中可接受的动词功能即可。

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

Because this is not hard to DIY. Just have a dictionary of accepted verbs to functions in each class.

def dispatcher(someObject, request):
    try:
      return someObject.acceptedVerbs[request.method]()
    except:
      return http.HttpResponseNotAllowed(someObject.acceptedVerbs.keys())
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文