Building WSGI Applications with WebOb Request and Response Objects

WebOb is a self-contained Python package that wraps the WSGI environment and response contract behind convenient, high-level objects. Instead of manipulating the raw environ dictionary and hand-crafting start-resposne tuples, you interact with rich Request and Response classes that expose headers, form data, cookies, status codes, and more as plain Python attributes.

Why WebOb?

  • HTTP semantics are mapped to native data structures (dicts, lists, bytes).
  • Battle-testeed code hides the quirky corners of PEP 3333.
  • Zero open issues; regressions are fixed quickly.
  • Full statement and branch coverage in the test suite.
  • No external dependencies beyond the standard library.
  • Runs on Python 3.7+.

From Raw WSGI to WebOb

A minimal WSGI callable looks like this:

from wsgiref.simple_server import make_server

def bare_wsgi(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/plain')])
    lines = ['Hello from raw WSGI']
    for k, v in sorted(environ.items()):
        lines.append(f'{k}: {v}')
    return ['\n'.join(lines).encode('utf-8')]

if __name__ == '__main__':
    port = 8000
    httpd = make_server('', port, bare_wsgi)
    print(f'Listening on http://0.0.0.0:{port}')
    httpd.serve_forever()

Query it with curl http://localhost:8000 and you’ll see the headers and CGI variables dumped back.

Replacing Boilerplate with Request and Response

WebOb’s objects absorb the repetitive parts:

from wsgiref.simple_server import make_server
from webob import Request, Response

def webob_app(environ, start_response):
    req = Request(environ)

    payload = ['Hello from WebOb']
    payload.extend(f'{k}: {v}' for k, v in sorted(req.environ.items()))

    resp = Response(text='\n'.join(payload), content_type='text/plain')
    return resp(environ, start_response)

if __name__ == '__main__':
    make_server('', 8000, webob_app).serve_forever()

The output is identical to the raw version, but the code is shorter and clearer.

Using the wsgify Decorator

The webob.dec.wsgify decorator hides the environ/start_response signature entirely. Your functon receives a Request and returns a Response (or something convertible to one).

from wsgiref.simple_server import make_server
from webob import Request, Response
from webob.dec import wsgify

@wsgify
def decorated_app(req: Request) -> Response:
    body = ['Hello from wsgify']
    body += [f'{k}: {v}' for k, v in sorted(req.environ.items())]
    return Response('\n'.join(body), content_type='text/plain')

if __name__ == '__main__':
    make_server('', 8000, decorated_app).serve_forever()

Again, curl http://localhost:8000 yields the same result, yet the application layer is now a single, testable function.

Tags: WebOb WSGI python HTTP request

Posted on Mon, 28 Sep 2026 16:17:18 +0000 by Gecko24