views:

146

answers:

1

I am using this middleware to make my app restful, but it looks like my form parameters are not coming through:

from google.appengine.ext import webapp

class RestHTTPMiddleware(object):
  def __init__(self, app):
    self.app = app

  def __call__(self, environ, start_response):
    method = webapp.Request(environ).get('_method')
    request = Request(environ)
    environ['wsgi.input'] = StringIO.StringIO(request.body)

    if method:
      environ['REQUEST_METHOD'] = method.upper()

    return self.app(environ, start_response)

when I submit a form and the debug it using:

def put(self):
    logging.debug(self.request.get('description'))

the logger is empty. The put(self) method is being called, I have tested it using the logger and my debug message is shown.

2nd revision:

from google.appengine.ext import webapp
from webob import Request
import logging
import StringIO

class RestHTTPMiddleware(object):
  def __init__(self, app):
    self.app = app

  def __call__(self, environ, start_response):
    request = Request(environ)
    environ['wsgi.input'] = StringIO.StringIO(request.body)

    method = webapp.Request(environ).get('_method')

    if method:
      environ['REQUEST_METHOD'] = method.upper()

    return self.app(environ, start_response)

Latest changes:

from google.appengine.ext import webapp
from webob import Request
import logging
import StringIO

class RestHTTPMiddleware(object):
  def __init__(self, app):
    self.app = app

  def __call__(self, environ, start_response):
    request = Request(environ)
    body = StringIO.StringIO(request.body)

    method = webapp.Request(environ).get('_method', None)

    if method:
      environ['REQUEST_METHOD'] = method.upper()
      environ['wsgi.input'] = body

    return self.app(environ, start_response)
+3  A: 

Instantiating webapp.Request and calling .get on it causes it to read the request body and parse form parameters in it. When your actual webapp starts later, it instantiates another request object, and once again tries to read the request body - but it's already been read, so nothing is returned.

You could modify your middleware to store a copy of the request body and put it back in the WSGI environment. There are other options:

  • If the _method arg will always be a query string parameter instead of a posted form parameter, you can use webapp.Request(environ).GET.get('method'), which won't read the request body.
  • You could override WSGIApplication.call to change how it does dispatch.
  • You could subclass WSGIApplication and supply a custom REQUEST_CLASS, being a function that builds a real request object, then modifies the environment that was passed in to suit you (hack!).
  • You could define a custom webapp RequestHandler base class that implements initialize(), reaching in to the request object's WSGI dict and changing the method (hack!).
Nick Johnson
Noop question, but how do I copy the request body and put it back in the WSGI environment?
kristian nissen
You'd need to do something like `environ['wsgi.input'] = StringIO.StringIO(request.body)`
Nick Johnson
Thank you. I have changed the codefrom google.appengine.ext import webappfrom webob import Requestimport loggingimport StringIOclass RestHTTPMiddleware(object): def __init__(self, app): self.app = app def __call__(self, environ, start_response): request = Request(environ) environ['wsgi.input'] = StringIO.StringIO(request.body) method = webapp.Request(environ).get('_method') if method: environ['REQUEST_METHOD'] = method.upper() return self.app(environ, start_response)but the result is the same
kristian nissen
Comments don't preserve newlines, so I have no idea what that is, sorry. I think the approach of fiddling with the wsgi environment is the weakest of the options, though, particularly if this is a query string parameter, rather than a POST argument.
Nick Johnson
I've updated the code block, it now includes environ['wsgi.input'] = StringIO.StringIO(request.body)
kristian nissen
You need to set wsgi.input _after_ all your operations using the Request object.
Nick Johnson
I have updated the question with the latest change, but still no result
kristian nissen
Any chance that you can help me out here?
kristian nissen
I honestly don't understand why you're trying to do this - it seems like the most complex option possible. Overriding call() on a base handler, or modifying WSGIApplication in a subclass seem like much better options.
Nick Johnson
Ok thank you. I have seen http://blog.notdot.net/2010/01/Webapps-on-App-Engine-part-3-Request-handlers and will try the same approach - Great blog by the way, I assume it's yours
kristian nissen