Hey All,

  This is somewhat related to the ticket “Process HTTP PUT into 
request.FILES and request.PUT as done for POST” [1], although much broader 
in scope.

  In that ticket there’s a link to a related discussion [2] where Malcolm 
Tredinnick explains why a request.PUT that mirrors the current request.POST 
behaviour isn’t the right thing to do.  Obviously Malcom’s right that 
request.POST is a special case that’s limited to supporting browser form 
submissions, which means restricting it’s behaviour to requests with a 
method “POST” [3] and a content type of either 
“application/x-www-form-urlencoded” or “multipart/form-data” (and apparently 
“text/plain” as per HTML5) [4].

  However, it would still be useful if Django provided built-in behaviour to 
be able to support accessing the content of HTTP requests for requests other 
than just form POSTs, at some higher level than simply forcing the developer 
to use request.read() or request.raw_post_content.

  I don’t want to get too bogged down in implementation details at this 
point, more just gauge a response to the proposal - if it’s a “that sounds 
like a decent idea”, or if it’s a “ain’t never gonna happen in core” kind of 
thing, so I’ll (try!) to keep it reasonably high level for now...


So...

  Generic Content-Type support could be provided either by re-purposing the 
request.REQUEST attribute (obviously making sure it’s done in a backwards 
compatible way [*]), or by adding a new lazily evaluated request.CONTENT.

  The request.REQUEST/request.CONTENT would be used to return the parsed 
content of the request, as determined by a set of installed parsers.  A 
request.content_parsers attribute would also be added to the request [**], 
which would default to initializing from settings.CONTENT_PARSERS, and would 
used to determine which parser would be used to de-serialize the request.  
  
  Django could initially ship with a FormParser (And perhaps also some other 
useful defaults such as a JSONParser.)  The default for 
settings.CONTENT_PARSERS would probably just include the FormParser.

  The FormParser would handle “application/x-www-form-urlencoded” and 
“multipart/form-data requests”, and return a QueryDict containing both the 
content data and uploaded files, reusing the existing MultiPartParser in 
it's implementation. (and respecting the existing upload_handlers for file 
uploads.)
  
  You would probably also want to add a ‘content_parsers’ view decorator 
that could be used to set the list of parsers used on a given request at the 
view level.  That’d mean that usage would look something like this...

def standard_view(request, *arg, **kwargs):
  request.POST[‘foo’] # Does not work for form PUT requests
  request.REQUEST[‘foo’]  # Now works for form PUT requests

@content_parsers(JSONParser)   # NB. Decorator would take either a 
tuple/list or a single value
def json_view(request, *args, **kwargs):
  request.REQUEST[‘foo’]  # Works for application/json requests

@content_parsers(BinaryUploadParser)
def put_upload_view(request, *args, **kwargs):
  uploaded_file = request.REQUEST  # An UploadedFile object

(*NB, I’m not suggesting here that Parsers would be limited to returning 
pre-parsed objects, you might also create streaming parsers that return 
generator objects.)


  I like the idea because it follows the same pattern as request.POST but 
supplements the behaviour to support arbitrary methods and content-types, 
which is pretty core stuff for anyone trying to do any Web API stuff with 
Django.  Forcing the developer to drop down to 
request.raw_post_content/request.read() isn’t particularly helpful if we 
could provide a nice flexible out of the box solution.

  Obviously there’d be a lots to thrash out, and there’s also some fiddly 
complexity there (EG handling request.raw_post_content, see ticket 9054 [5]) 
but what do you peeps think of the idea in general?  Is it something that’d 
be valuable in core, would it only merit further discussion if there was a 
more explicitly detailed proposal, or if there was a reference 
implementation, or is it entirely out-of-scope?  All of what I’ve mentioned 
could equally be implemented as Middleware, so would that make more sense 
than implementing it in core, and if it was done as Middleware would it 
still be useful for inclusion into Django?

  I realise it’s a fairly big proposal (esp. for a first post to the list) 
so feel free to go ahead and shoot me right down!  :)

  Cheers,

    Tom


Possible interface
==================

settings.CONTENT_PARSERS    # List of parsers, defaults to 
(contentparsers.FormParser,)

HttpRequest.parsers     # property, defaults to initializing from 
settings.CONTENT_PARSERS
HttpRequest.REQUEST *or* .CONTENT   # lazily evalulated, like request.POST

class ContentParser(object)  # Interface for parser objects.
  handles_request(request)
  parse(request)

class FormParser(ContentParser)  # Multipart and urlencoded behaviour, 
probably returns QueryDict containing both form fields and files.
class JsonParser(ContentParser)  # Optional, return dict.
class BinaryUploadParser(ContentParser) # Optional, returns UploadedFile
# Possibly a GetParamsParser or BackwardsCompatParser if repurposing 
request.REQUEST.

content_parsers  # View decorator, sets .parsers on request

BadRequestException # Thrown on malformed request content, 400 response
UnsupportedMediaTypeException # Thrown if Content-Type on request is not 
supported by set of content_parsers, 415 response


Notes
=====

  [*] EG The default for settings.CONTENT_PARSERS could be 
(BackwardsCompatParser,).
         Alternatively we could provide a GetParamsParser, and have the 
default for settings.CONTENT_PARSERS be (GetParamsParser, FormParser). 
 That's slightly different in that the GetParamsParser wouldn't be parsing 
the request body like the other proposed parsers, but it might be quite nice 
in that request.REQUEST would continue to provide (almost) the existing 
behaviour, but in a more flexible, useful way.
         A possible migration might be to use 
CONTENT_PARSERS=(BackwardCompatParser,) as the default in 1.4 and move to 
CONTENT_PARSERS=(GetParamsParser, FormParser) in 1.5.

  [**] request.content_parsers would probably be implemented in the same 
pattern as request.upload_handlers

  [1] http://code.djangoproject.com/ticket/12635
  [2] 
https://groups.google.com/d/topic/django-developers/dxI4qVzrBY4/discussion
  [3] HTML4: http://www.w3.org/TR/html401/interact/forms.html#h-17.13.1
       HTML5: http://dev.w3.org/html5/spec/Overview.html#attr-fs-method
  [4] HTML4: http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4
       HTML5: http://dev.w3.org/html5/spec/Overview.html#attr-fs-enctype
  [5] http://code.djangoproject.com/ticket/9054

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers" group.
To post to this group, send email to django-developers@googlegroups.com.
To unsubscribe from this group, send email to 
django-developers+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/django-developers?hl=en.

Reply via email to