app: Support multiple resource representations

The `VariedResponse` class essentially turns the website into a REST
API. When a controller returns a response object that is an instance of
this class, the response representation sent to the user-agent will vary
based on the value of the `Accept` request header. Specifically,
requests containing `Accept: application/json` will receive a
JSON-encoded object, while typical browser requests will get an (X)HTML
document.
master
Dustin 2017-07-02 11:32:28 -05:00
parent bb9f19ff04
commit 860c36b0f2
2 changed files with 74 additions and 2 deletions

View File

@ -1,7 +1,12 @@
from milla.dispatch import routing from milla.dispatch import routing
from . import gallery, thumbnails from . import (
import os base,
gallery,
thumbnails,
)
import functools
import milla.util import milla.util
import os
DEFAULT_CONFIG = { DEFAULT_CONFIG = {
@ -30,3 +35,21 @@ class Application(milla.Application):
r.add_route('/', gallery.GalleryController()) r.add_route('/', gallery.GalleryController())
r.add_route('/thumbnails/{image}', thumbnails.ThumbnailController()) r.add_route('/thumbnails/{image}', thumbnails.ThumbnailController())
def make_request(self, environ):
request = super(Application, self).make_request(environ)
default_format = base.VariedResponse.default_content_type
offers = [
'application/json',
'application/xhtml+xml',
'text/html',
]
want_fmt = request.accept.best_match(offers, default_format)
if want_fmt == 'application/json':
request.want = 'json'
elif want_fmt == 'text/html':
request.want = 'html'
else:
request.want = 'xhtml'
request.ResponseClass = functools.partial(base.VariedResponse, request)
return request

49
src/dcow/base.py Normal file
View File

@ -0,0 +1,49 @@
import jinja2
import json
import milla
class Template(object):
LOADER = jinja2.PackageLoader(__name__.rsplit('.', 1)[0])
def __init__(self, name):
self.env = jinja2.Environment(loader=self.LOADER)
self.tmpl = self.env.get_template(name)
def render(self, context):
return self.tmpl.render(**context)
class VariedResponse(milla.Response):
def __init__(self, request, *args, **kwargs):
super(VariedResponse, self).__init__(*args, **kwargs)
self.request = request
def set_payload(self, template, context=None):
if not self.vary:
self.vary = ['Accept']
elif 'accept' not in (v.lower() for v in self.vary):
self.vary = self.vary + ('Accept',)
if context is None:
context = {}
if template is None or self.request.want == 'json':
self.content_type = str('application/json')
json.dump(context, self.body_file)
else:
if self.request.want == 'xhtml':
self.content_type = str('application/xhtml+xml')
self.render(template, context)
def render(self, template, context=None):
if context is None:
context = {}
t = Template(template)
t.env.globals.update(
url=self.request.create_href,
static=self.request.static_resource,
)
if hasattr(self.request, 'user'):
t.env.globals['user'] = self.request.user
self.text = t.render(context)