🐛 improve exception handling
This commit is contained in:
parent
8e37e58a99
commit
23360a3d91
@ -1,4 +1,5 @@
|
||||
from spiderweb import WebServer
|
||||
from spiderweb.exceptions import ServerError
|
||||
from spiderweb.response import HttpResponse, JsonResponse, TemplateResponse, RedirectResponse
|
||||
|
||||
|
||||
@ -30,7 +31,7 @@ def json(request):
|
||||
|
||||
@app.route("/error")
|
||||
def error(request):
|
||||
return HttpResponse(status_code=500, body="Internal Server Error")
|
||||
raise ServerError
|
||||
|
||||
|
||||
@app.route("/middleware")
|
||||
|
@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "spiderweb"
|
||||
version = "0.4.0"
|
||||
version = "0.5.0"
|
||||
description = "A small web framework, just big enough to hold your average spider."
|
||||
authors = ["Joe Kaufeld <opensource@joekaufeld.com>"]
|
||||
readme = "README.md"
|
||||
|
@ -1,6 +1,7 @@
|
||||
class SpiderwebException(Exception):
|
||||
# parent error class; all child exceptions should inherit from this
|
||||
pass
|
||||
def __str__(self):
|
||||
return f"{self.__class__.__name__}({self.code}, {self.msg})"
|
||||
|
||||
|
||||
class SpiderwebNetworkException(SpiderwebException):
|
||||
@ -11,14 +12,46 @@ class SpiderwebNetworkException(SpiderwebException):
|
||||
self.msg = msg
|
||||
self.desc = desc
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.__class__.__name__}({self.code}, {self.msg})"
|
||||
|
||||
|
||||
class APIError(SpiderwebNetworkException):
|
||||
pass
|
||||
|
||||
|
||||
class NotFound(SpiderwebNetworkException):
|
||||
def __init__(self):
|
||||
self.code = 404
|
||||
self.msg = "Not Found"
|
||||
self.desc = "The requested resource could not be found"
|
||||
|
||||
|
||||
class BadRequest(SpiderwebNetworkException):
|
||||
def __init__(self, desc=None):
|
||||
self.code = 400
|
||||
self.msg = "Bad Request"
|
||||
self.desc = desc if desc else "The request could not be understood by the server"
|
||||
|
||||
|
||||
class Unauthorized(SpiderwebNetworkException):
|
||||
def __init__(self, desc=None):
|
||||
self.code = 401
|
||||
self.msg = "Unauthorized"
|
||||
self.desc = desc if desc else "The request requires user authentication"
|
||||
|
||||
|
||||
class Forbidden(SpiderwebNetworkException):
|
||||
def __init__(self, desc=None):
|
||||
self.code = 403
|
||||
self.msg = "Forbidden"
|
||||
self.desc = desc if desc else "You are not allowed to access this resource"
|
||||
|
||||
|
||||
class ServerError(SpiderwebNetworkException):
|
||||
def __init__(self, desc=None):
|
||||
self.code = 500
|
||||
self.msg = "Internal Server Error"
|
||||
self.desc = desc if desc else "The server has encountered an error"
|
||||
|
||||
|
||||
class ConfigError(SpiderwebException):
|
||||
pass
|
||||
|
||||
|
@ -5,7 +5,6 @@
|
||||
import json
|
||||
import re
|
||||
import signal
|
||||
import sys
|
||||
import time
|
||||
import traceback
|
||||
from http.server import BaseHTTPRequestHandler, HTTPServer
|
||||
@ -23,7 +22,7 @@ from spiderweb.exceptions import (
|
||||
ConfigError,
|
||||
ParseError,
|
||||
GeneralException,
|
||||
NoResponseError, UnusedMiddleware,
|
||||
NoResponseError, UnusedMiddleware, SpiderwebNetworkException, NotFound,
|
||||
)
|
||||
from spiderweb.request import Request
|
||||
from spiderweb.response import HttpResponse, JsonResponse, TemplateResponse, RedirectResponse
|
||||
@ -142,11 +141,8 @@ class WebServer(HTTPServer):
|
||||
raise ParseError(f"Unknown converter {converter}")
|
||||
else:
|
||||
converter = StrConverter # noqa: F405
|
||||
parts[i] = r"(?P<%s>%s)" % (
|
||||
f"{name}__{str(converter.__name__)}",
|
||||
converter.regex,
|
||||
)
|
||||
return re.compile(r"^%s$" % "/".join(parts))
|
||||
parts[i] = rf"(?P<{name}__{str(converter.__name__)}>{converter.regex})"
|
||||
return re.compile(rf"^{'/'.join(parts)}$")
|
||||
|
||||
def check_for_route_duplicates(self, path: str):
|
||||
if self.convert_path(path) in self.handler_class._routes:
|
||||
@ -233,6 +229,9 @@ class WebServer(HTTPServer):
|
||||
class RequestHandler(BaseHTTPRequestHandler):
|
||||
# I can't help the naming convention of these because that's what
|
||||
# BaseHTTPRequestHandler uses for some weird reason
|
||||
|
||||
# These stop pycharm from complaining about these not existing. They're
|
||||
# injected by the WebServer class at runtime
|
||||
_routes = {}
|
||||
middleware = []
|
||||
|
||||
@ -269,7 +268,7 @@ class RequestHandler(BaseHTTPRequestHandler):
|
||||
return self._routes[option], convert_match_to_dict(
|
||||
match_data.groupdict()
|
||||
)
|
||||
raise APIError(404, "No route found")
|
||||
raise NotFound()
|
||||
|
||||
def get_error_route(self, code: int) -> Callable:
|
||||
try:
|
||||
@ -319,10 +318,10 @@ class RequestHandler(BaseHTTPRequestHandler):
|
||||
self.middleware.remove(middleware)
|
||||
continue
|
||||
|
||||
def prepare_response(self, request, resp) -> HttpResponse:
|
||||
def prepare_and_fire_response(self, request, resp) -> None:
|
||||
try:
|
||||
if isinstance(resp, dict):
|
||||
self.fire_response(JsonResponse(data=resp))
|
||||
self.fire_response(request, JsonResponse(data=resp))
|
||||
if isinstance(resp, TemplateResponse):
|
||||
if hasattr(self, "env"): # injected from above
|
||||
resp.set_template_loader(self.env)
|
||||
@ -335,17 +334,25 @@ class RequestHandler(BaseHTTPRequestHandler):
|
||||
except APIError:
|
||||
raise
|
||||
|
||||
except ConnectionAbortedError as e:
|
||||
log.error(f"GET {self.path} : {e}")
|
||||
except Exception:
|
||||
log.error(traceback.format_exc())
|
||||
self.fire_response(request, self.get_error_route(500)(request))
|
||||
|
||||
def send_error_response(self, request: Request, e: SpiderwebNetworkException):
|
||||
try:
|
||||
self.send_error(e.code, e.msg, e.desc)
|
||||
except ConnectionAbortedError as e:
|
||||
log.error(f"{request.method} {self.path} : {e}")
|
||||
|
||||
def handle_request(self, request):
|
||||
|
||||
request.url = urlparse.urlparse(request.path)
|
||||
|
||||
try:
|
||||
handler, additional_args = self.get_route(request.url.path)
|
||||
except NotFound:
|
||||
handler = self.get_error_route(404)
|
||||
additional_args = {}
|
||||
|
||||
if request.url.query:
|
||||
params = urlparse.parse_qs(request.url.query)
|
||||
@ -363,18 +370,9 @@ class RequestHandler(BaseHTTPRequestHandler):
|
||||
resp = handler(request, **additional_args)
|
||||
if resp is None:
|
||||
raise NoResponseError(f"View {handler} returned None.")
|
||||
if isinstance(resp, dict):
|
||||
self.fire_response(request, JsonResponse(data=resp))
|
||||
if isinstance(resp, TemplateResponse):
|
||||
if hasattr(self, "env"): # injected from above
|
||||
resp.set_template_loader(self.env)
|
||||
|
||||
self.process_response_middleware(request, resp)
|
||||
self.fire_response(request, resp)
|
||||
# run the response through the middleware and send it
|
||||
self.prepare_and_fire_response(request, resp)
|
||||
else:
|
||||
raise APIError(404)
|
||||
except APIError as e:
|
||||
try:
|
||||
self.send_error(e.code, e.msg, e.desc)
|
||||
except ConnectionAbortedError as e:
|
||||
log.error(f"GET {self.path} : {e}")
|
||||
raise SpiderwebNetworkException(404)
|
||||
except SpiderwebNetworkException as e:
|
||||
self.send_error_response(request, e)
|
||||
|
Loading…
Reference in New Issue
Block a user