Routing and handlers
====================

MicroPie maps incoming HTTP requests to methods on your :class:`~micropie.App`
subclass.  This section explains how the mapping works and how your
handlers receive input from the URL path, query strings, form data and
sessions.

URL to function mapping
-----------------------

When an HTTP request arrives, MicroPie extracts the path portion of the
URL and uses the first path segment to determine which method should
handle the request.  For example, a GET request to ``/greet`` calls
``greet`` on your :class:`~micropie.App` subclass.  A request to the
root URL ``/`` calls ``index``.  Only methods that do not start with
an underscore are exposed as route handlers.  Prefacing a method name
with an underscore makes it private and hides it from external
requests.

Handlers may be synchronous or asynchronous functions.  They return
either a string, bytes, a JSON‑serialisable object, a tuple of
``(status_code, body)`` or ``(status_code, body, headers)``, or an
iterator/generator for streaming responses.  See
:doc:`../howto/streaming` for details on streaming.

Parameters and arguments
------------------------

MicroPie automatically populates handler arguments from several
sources in the following order:

1. **Path parameters** – Additional path segments after the first map to
   positional parameters.  For example:

   .. code-block:: python

      class AppExample(App):
          async def greet(self, name):
              return f"Hello, {name}!"

   Visiting ``/greet/Alice`` passes ``"Alice"`` as the ``name`` argument.
   You can also use ``*args`` to capture an arbitrary number of path
   segments.

2. **Query parameters** – Named parameters in the query string fill
   keyword arguments when path parameters are exhausted.  For example,
   ``/greet?name=Alice`` also passes ``"Alice"`` to the ``name``
   parameter.

3. **Body/form data** – For POST, PUT and PATCH requests, form fields
   populate handler arguments.  JSON bodies are decoded into a
   dictionary of key/value pairs.

4. **Files** – If the request is multipart/form‑data, uploaded files
   appear in :attr:`~micropie.Request.files`.  You can declare a file
   argument in your handler signature to receive a file object.

5. **Session data** – Values stored in :attr:`~micropie.Request.session`
   fill remaining parameters.  See :doc:`../howto/sessions` for details.

6. **Default values** – If no other source provides a value, default
   values in your function signature are used.

If MicroPie cannot determine a value for a required parameter, it
returns a ``400 Bad Request``.

Helpers and raw request data
----------------------------

In handlers, you can access request input through helper methods or
through raw parsed attributes:

* Helpers:

  * ``self.request.query(name, default=None)`` returns the first query
    value.
  * ``self.request.form(name, default=None)`` returns the first
    form/body value.
  * ``self.request.json(name=None, default=None)`` returns either the
    full parsed JSON payload or a key from a top-level JSON object.

* Raw attributes:

  * ``self.request.path_params``: list of positional path segments.
  * ``self.request.query_params``: ``dict[str, list[str]]`` parsed from
    the query string.
  * ``self.request.body_params``: ``dict[str, list[str]]`` parsed from
    form-urlencoded payloads, or mirrored from top-level JSON objects.
  * ``self.request.get_json``: full parsed JSON payload.
  * ``self.request.session``: session dictionary.
  * ``self.request.files``: uploaded multipart files.

The helper APIs are convenient when you need a single value.  The raw
attributes are useful when you need full multi-value access or exact
payload inspection.

Examples
--------

The following examples illustrate common patterns:

.. code-block:: python

   class MyApp(App):
       async def greet(self, name="Guest"):
           """Return a greeting for a named visitor.

           If the ``name`` argument is not provided via the path or
           query parameters, ``"Guest"`` is used as a default.
           """
           return f"Hello, {name}!"

       async def add(self, x, y):
           """Add two numbers provided via path segments.

           Example: ``/add/2/3`` returns ``5``.
           """
           return str(int(x) + int(y))

       async def submit(self, username="Anonymous"):
           """Handle a form submission.

           This handler accepts POST requests and uses the
           ``username`` field from the request body.
           """
           return f"Form submitted by: {username}"

See :ref:`Request objects <request-object>` for the attributes
available on the current request and :ref:`App class <app-class>` for
details about the ``App`` class.

HTTP methods and responses
--------------------------

Handlers run for any HTTP method unless you explicitly check
``self.request.method`` inside the handler.  It is your
responsibility to dispatch based on the method if your endpoint should
behave differently for GET and POST.  Handlers may return:

* A string or bytes – sent as the body of the response with a
  ``200 OK`` status.
* A JSON‑serialisable object – automatically encoded into JSON and
  returned with a ``Content‑Type`` of ``application/json``.
* A tuple ``(status_code, body)`` – sets the HTTP status and body.
* A tuple ``(status_code, body, headers)`` – sets status, body and
  additional headers.  Headers should be a list of ``(name, value)`` pairs.
* A generator or async generator – streams chunks to the client.  Use
  this for large responses or server‑sent events.

Advanced routing
----------------

You can implement explicit routing, path prefixing or complex
dispatching by writing a custom :class:`~micropie.HttpMiddleware`.  See
the :doc:`../howto/middleware` guide and the examples in the
``examples/explicit_routing`` directory of the source distribution.