11"""Several gateway handlers"""
22import typing as t
33
4- from werkzeug .datastructures import Headers
4+ from werkzeug .datastructures import Headers , MultiDict
55from werkzeug .exceptions import HTTPException , NotFound
66
77from rolo .response import Response
1212
1313class RouterHandler :
1414 """
15- Adapter to serve a ``Router`` as a ``Handler``.
15+ Adapter to serve a ``Router`` as a ``Handler``. The handler takes from the ``RequestContext`` the ``Request``
16+ object, and dispatches it via ``Router.dispatch``. The ``Response`` object that call returns, is then merged into
17+ the ``Response`` object managed by the handler chain. If the router returns a response, the ``HandlerChain`` is
18+ stopped.
19+
20+ If the dispatching raises a ``NotFound`` (because there is no route in the Router to match the request), the chain
21+ will respond with 404 and "not found" as string, given that ``respond_not_found`` is set to True. This is to
22+ provide a simple, default way to handle 404 messages. In most cases, you will want your own 404 error handling
23+ in the handler chain, which is why ``respond_not_found`` is set to ``False`` by default.
1624 """
1725
1826 router : Router
@@ -34,14 +42,40 @@ def __call__(self, chain: HandlerChain, context: RequestContext, response: Respo
3442
3543class EmptyResponseHandler :
3644 """
37- Handler that creates a default response if the response in the context is empty.
45+ Handler that creates a default response if the response in the context is empty. A response is considered empty
46+ if its status code is set to 0 or None, and the response body is empty. Since ``Response`` is initialized with a
47+ 200 status code by default, you'll have to explicitly set the status code to 0 or None in your handler chain.
48+ For example::
49+
50+ def init_response(chain, context, response):
51+ response.status_code = 0
52+
53+ def handle_request(chain, context, response):
54+ if context.request.path == "/hello"
55+ chain.respond("hello world")
56+
57+ gateway = Gateway(request_handlers=[
58+ init_response,
59+ handle_request,
60+ EmptyResponseHandler(404, body=b"not found")
61+ ])
62+
63+ This handler chain will return 404 for all requests except those going to ``http://<server>/hello``.
3864 """
3965
4066 status_code : int
4167 body : bytes
42- headers : dict
68+ headers : t . Mapping [ str , t . Any ] | MultiDict [ str , t . Any ] | Headers
4369
4470 def __init__ (self , status_code : int = 404 , body : bytes = None , headers : Headers = None ):
71+ """
72+ Creates a new EmptyResponseHandler that will populate the ``Response`` object with the given values, if the
73+ response was previously considered empty.
74+
75+ :param status_code: The HTTP status code to use (defaults to 404)
76+ :param body: The body to use as response (defaults to empty string)
77+ :param headers: The additional headers to set for the response
78+ """
4579 self .status_code = status_code
4680 self .body = body or b""
4781 self .headers = headers or Headers ()
@@ -60,7 +94,52 @@ def populate_default_response(self, response: Response):
6094
6195
6296class WerkzeugExceptionHandler :
97+ """
98+ Convenience handler that translates werkzeug exceptions into HTML or JSON responses. Werkzeug exceptions are
99+ raised by ``Router`` instances, but can also be useful to use in your own handlers. These exceptions already
100+ contain a human-readable name, description, and an HTML template that can be rendered. The handler also supports
101+ a rolo-specific JSON format.
102+
103+ For example, this handler chain::
104+
105+ from werkzeug.exceptions import NotFound
106+
107+ def raise_not_found(chain, context, response):
108+ raise NotFound()
109+
110+ gateway = Gateway(
111+ request_handlers=[
112+ raise_not_found,
113+ ],
114+ exception_handlers=[
115+ WerkzeugExceptionHandler(output_format="html"),
116+ ]
117+ )
118+
119+ Would always yield the following HTML::
120+
121+ <!doctype html>
122+ <html lang=en>
123+ <title>404 Not Found</title>
124+ <h1>Not Found</h1>
125+ The requested URL was not found on the server. If you entered the URL manually please check
126+ your spelling and try again.
127+
128+ Or if you use JSON (via ``WerkzeugExceptionHandler(output_format="json")``)::
129+
130+ {
131+ "code": 404,
132+ "description": "The requested URL was not found on the server. [...]"
133+ }
134+ """
135+
63136 def __init__ (self , output_format : t .Literal ["json" , "html" ] = None ) -> None :
137+ """
138+ Create a new ``WerkzeugExceptionHandler`` to use as exception handler in a handler chain.
139+
140+ :param output_format: The output format in which to render the exception into the response (either ``html``
141+ or ``json``), defaults to ``json``.
142+ """
64143 self .format = output_format or "json"
65144
66145 def __call__ (
@@ -78,6 +157,7 @@ def __call__(
78157 chain .respond (
79158 status_code = exception .code ,
80159 headers = headers ,
160+ # TODO: add name
81161 payload = {"code" : exception .code , "description" : exception .description },
82162 )
83163 else :
0 commit comments