11

I have a Django project where I'm using a view to handle different HTTP methods. The POST handled the creation of an object and then redirected to the same view as a GET (or so I thought), using Django's redirect() shortcut (HTTPResponseRedirect) to return the newly created object. This worked fine. I tried the same thing with a PUT but I fell into a redirect loop. After scratching my head for a while I stumbled across this SO answer and then I inferred that since redirect doesn't handle the POST data, the request turns into a GET.

I confirmed this by watching the logs when I do the redirect from a POST:

[15/Dec/2014 00:47:43] "POST /client/151/ HTTP/1.1" 302 0
[15/Dec/2014 00:47:43] "GET /client/151/ HTTP/1.1" 200 395

However the PUT stays a PUT and throws me into a redirect loop until it errors out.

[14/Dec/2014 23:07:36] "PUT /api/asset/6779 HTTP/1.1" 301 0
[14/Dec/2014 23:07:37] "PUT /api/asset/6779/ HTTP/1.1" 302 0
[14/Dec/2014 23:07:37] "PUT /api/asset/6779 HTTP/1.1" 301 0
[14/Dec/2014 23:07:38] "PUT /api/asset/6779/ HTTP/1.1" 302 0
[14/Dec/2014 23:07:38] "PUT /api/asset/6779 HTTP/1.1" 301 0
[14/Dec/2014 23:07:39] "PUT /api/asset/6779/ HTTP/1.1" 302 0
[14/Dec/2014 23:07:39] "PUT /api/asset/6779 HTTP/1.1" 301 0
[14/Dec/2014 23:07:40] "PUT /api/asset/6779/ HTTP/1.1" 302 0
[14/Dec/2014 23:07:40] "PUT /api/asset/6779 HTTP/1.1" 301 0
[14/Dec/2014 23:07:41] "PUT /api/asset/6779/ HTTP/1.1" 302 0
[14/Dec/2014 23:07:41] "PUT /api/asset/6779 HTTP/1.1" 301 0
[14/Dec/2014 23:07:42] "PUT /api/asset/6779/ HTTP/1.1" 302 0

Shouldn't the redirect use GET? I understand what's going on but not sure why? What gives?

EDIT

# urls.py
url(r'^$', views.put_vs_post_redirect),

# views.py
from django.shortcuts import redirect

def put_vs_post_redirect(request, asset_id):

    if request.method == 'GET':
        return HTTPResponse('Get request')     
    elif request.method == 'POST':
        return redirect('/')
    elif request.method == 'PUT':
        return redirect('/')
Community
  • 1
  • 1
hanleyhansen
  • 6,304
  • 8
  • 37
  • 73
  • 2
    Related: http://stackoverflow.com/questions/13628831/apache-301-redirect-and-preserving-post-data – Ignacio Vazquez-Abrams Dec 15 '14 at 06:15
  • How are you making the user agent perform a PUT? A redirect to GET from a POST makes sense, because you can make a browser browse to a page using POST (form), but not by PUT. It seems you are writing an API. Why not just return the URI to the created object in a 200 OK response (or just document it is where you PUT it) and have the user agent act on that? Also, you POST with a trailing slash and PUT without. Is that intentional? – Anton Dec 15 '14 at 17:50
  • @Anton great points. Thanks. Your workaround is acceptable but what i was trying to accomplish is, when the PUT is done I return the new object so I redirect to the GET which is designed to give me an object. At this point I have a workaround but I'm more interested in figuring out if this is Django's doing or if this is done at the HTTP level. – hanleyhansen Dec 15 '14 at 18:12
  • 1
    It's done at the HTTP level, specifically in the client implementation. – Ignacio Vazquez-Abrams Dec 16 '14 at 04:48

1 Answers1

6

As mentioned in the comments, this is entirely dependent on the client, and not all clients handle redirects in the same way. You can find a decent explanation of the redirect codes and why a 301 should drop POST data on Stack Overflow.

When working with a 301 (and often a 302) redirect, most browsers will discard POST data and make a GET request. This is mostly because browsers have always done this, and POST requests most commonly come from web forms, so it makes sense that the redirect results in a GET, allowing for the browser to display a different page without interfering. This is not the case for things like PUT or PATCH requests, as they cannot currently be sent by web forms and typically play by different rules.

If you are looking to maintain the POST data on a 302 redirect, you should consider using a 307 redirect instead. A 307 request should maintain the request method, and the request body as a result.

If you are looking to maintain the POST data in a 301 redirect, there is currently a draft for a 308 status code that would work like the 307, but be permanent.

You can force the redirect to use a GET request with a 303 redirect. It works very much like a 302, but it enforces that the request method is always a GET request. It's often used in APIs for asynchronous tasks.

Community
  • 1
  • 1
Kevin Brown-Silva
  • 40,873
  • 40
  • 203
  • 237