Introduce a middleware that allows for HttpResponses to be thrown from inner utility functions, to avoid boilerplate in views

This commit is contained in:
Danielle McLean 2018-07-03 09:41:00 +10:00
parent 1d4be082cf
commit 7d17a92793
Signed by: 00dani
GPG key ID: 8EB789DDF3ABD240
4 changed files with 22 additions and 9 deletions

View file

@ -1,35 +1,35 @@
from typing import Union
from urllib.parse import urlparse from urllib.parse import urlparse
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
from django.http import HttpResponse from django.http import HttpResponse
from django.urls import resolve, Resolver404 from django.urls import resolve, Resolver404
from micropub.views import error from micropub.views import error
from lemoncurry.middleware import ResponseException
from .models import Entry from .models import Entry
def from_url(url: str) -> Union[Entry, HttpResponse]: def from_url(url: str) -> Entry:
domain = Site.objects.get_current().domain domain = Site.objects.get_current().domain
if not url: if not url:
return error.bad_req('url parameter required') raise ResponseException(error.bad_req('url parameter required'))
if '//' not in url: if '//' not in url:
url = '//' + url url = '//' + url
parts = urlparse(url, scheme='https') parts = urlparse(url, scheme='https')
if parts.scheme not in ('http', 'https') or parts.netloc != domain: if parts.scheme not in ('http', 'https') or parts.netloc != domain:
return error.bad_req('url does not point to this site') raise ResponseException(error.bad_req('url does not point to this site'))
try: try:
match = resolve(parts.path) match = resolve(parts.path)
except Resolver404: except Resolver404:
return error.bad_req('url does not point to a valid page on this site') raise ResponseException(error.bad_req('url does not point to a valid page on this site'))
if match.view_name != 'entries:entry': if match.view_name != 'entries:entry':
return error.bad_req('url does not point to an entry on this site') raise ResponseException(error.bad_req('url does not point to an entry on this site'))
try: try:
entry = Entry.objects.get(pk=match.kwargs['id']) entry = Entry.objects.get(pk=match.kwargs['id'])
except Entry.DoesNotExist: except Entry.DoesNotExist:
return error.bad_req('url does not point to an existing entry') raise ResponseException(error.bad_req('url does not point to an existing entry'))
return entry return entry

14
lemoncurry/middleware.py Normal file
View file

@ -0,0 +1,14 @@
from django.http import HttpRequest, HttpResponse
from django.utils.deprecation import MiddlewareMixin
class ResponseException(Exception):
def __init__(self, response: HttpResponse) -> None:
self.response = response
class ResponseExceptionMiddleware(MiddlewareMixin):
def process_exception(self, request: HttpRequest, exception: Exception) -> HttpResponse:
if isinstance(exception, ResponseException):
return exception.response
raise exception

View file

@ -118,6 +118,7 @@ MIDDLEWARE = [
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
'django.contrib.sites.middleware.CurrentSiteMiddleware', 'django.contrib.sites.middleware.CurrentSiteMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
'lemoncurry.middleware.ResponseExceptionMiddleware',
] ]
ROOT_URLCONF = 'lemoncurry.urls' ROOT_URLCONF = 'lemoncurry.urls'

View file

@ -17,8 +17,6 @@ def delete(request):
return error.unsupported_type(request.content_type) return error.unsupported_type(request.content_type)
url = normalise[request.content_type](request) url = normalise[request.content_type](request)
entry = from_url(url) entry = from_url(url)
if isinstance(entry, HttpResponse):
return entry
if entry.author != request.token.user: if entry.author != request.token.user:
return error.forbid('entry belongs to another user') return error.forbid('entry belongs to another user')