# Enrutamiento

Source: https://docs.sisk-framework.org/es/docs/fundamentals/routing.html

El [Router](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Router.md) es el primer paso para construir el servidor. Es responsable de albergar objetos [Route](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Route.md), que son puntos finales que asignan URLs y sus métodos a acciones ejecutadas por el servidor. Cada acción se encarga de recibir una solicitud y entregar una respuesta al cliente.

Las rutas son pares de expresiones de ruta ("patrón de ruta") y el método HTTP al que pueden escuchar. Cuando se realiza una solicitud al servidor, éste intentará encontrar una ruta que coincida con la solicitud recibida, luego llamará a la acción de esa ruta y entregará la respuesta resultante al cliente.

Existen múltiples formas de definir rutas en Sisk: pueden ser estáticas, dinámicas o auto‑exploradas, definidas por atributos, o directamente en el objeto Router.

```cs
Router mainRouter = new Router();

// asigna la ruta GET / a la siguiente acción
mainRouter.MapGet("/", request => {
    return new HttpResponse("Hello, world!");
});
```

Para entender lo que una ruta es capaz de hacer, necesitamos entender lo que una solicitud es capaz de hacer. Un [HttpRequest](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.md) contendrá todo lo que necesitas. Sisk también incluye algunas características extra que aceleran el desarrollo en general.

Para cada acción recibida por el servidor, se llamará a un delegado del tipo [RouteAction](https://docs.sisk-framework.org/api/Sisk.Core.Routing.RouteAction.md). Este delegado contiene un parámetro que lleva un [HttpRequest](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.md) con toda la información necesaria sobre la solicitud recibida por el servidor. El objeto resultante de este delegado debe ser un [HttpResponse](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpResponse.md) o un objeto que se mapee a él mediante [tipos de respuesta implícitos](https://docs.sisk-framework.org/es/docs/fundamentals/responses.md#implicit-response-types).

## Matching routes

Cuando una solicitud es recibida por el servidor HTTP, Sisk busca una ruta que satisfaga la expresión del camino recibido por la solicitud. La expresión siempre se prueba entre la ruta y el camino de la solicitud, sin considerar la cadena de consulta.

La primera ruta cuyo camino y método coincidan con la solicitud la maneja. Cuando no se encuentra ninguna ruta que coincida con esa solicitud, se devuelve al cliente la respuesta de [Router.NotFoundErrorHandler](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Router.NotFoundErrorHandler.md). Cuando el patrón de ruta coincide, pero el método HTTP no, se envía al cliente la respuesta de [Router.MethodNotAllowedErrorHandler](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Router.MethodNotAllowedErrorHandler.md). El orden en que se prueban las rutas se describe en [matching precedence](#matching-precedence).

Sisk verifica la posibilidad de colisiones de rutas entre rutas estáticas. Cuando el servidor se inicia, Sisk buscará rutas estáticas cuyos patrones de ruta y métodos se solapen, y lanzará una excepción si se encuentra alguna. En la misma comprobación, Sisk lanza una `ArgumentException` si dos o más rutas comparten el mismo [Route.Name](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Route.Name.md). Ambas comprobaciones están controladas por [Router.CheckForRouteCollisions](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Router.CheckForRouteCollisions.md), que está habilitada por defecto.

### Matching precedence

Cada ruta es **estática** o **dinámica**, según lo indique [Route.IsStatic](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Route.IsStatic.md):

- **Rutas estáticas** siguen estrictamente su patrón de ruta, como `/users/<id>`. Las rutas creadas con los métodos `Map*`, instancias de `Route` y atributos de ruta son estáticas.
- **Rutas dinámicas** interpretan su camino a su manera y pueden coincidir con caminos que no pueden describirse mediante un patrón de ruta. [RegexRoute](https://docs.sisk-framework.org/api/Sisk.Core.Routing.RegexRoute.md), [PrefixRoute](https://docs.sisk-framework.org/api/Sisk.Core.Routing.PrefixRoute.md) (usado por [file servers](https://docs.sisk-framework.org/es/docs/features/file-server.md)) y [rutas de coincidencia personalizadas](#custom-route-matching) son dinámicas.

Cuando el servidor se inicia, el router reordena sus rutas una vez de modo que cada ruta estática se pruebe antes que cualquier ruta dinámica. Dentro de cada grupo, las rutas conservan su orden de registro. Esto significa que una ruta estática siempre tiene precedencia sobre una ruta dinámica, sin importar el orden en que fueron definidas:

```cs
// dinámico: sirve cualquier ruta GET del directorio "wwwroot"
mainRouter.MapFileSystem("/", "wwwroot");

// estático: maneja GET /api/status, aunque se definió después del servidor de archivos
mainRouter.MapGet("/api/status", request => new HttpResponse("ok"));
```

En el ejemplo anterior, el servidor de archivos actúa como respaldo para cualquier solicitud GET que no sea manejada por una ruta estática.

Los dos grupos también difieren en cómo se validan y transforman:

|                     | Rutas estáticas | Rutas dinámicas |
|---------------------|-----------------|-----------------|
| Orden de coincidencia | Primero, en orden de registro | Después de todas las rutas estáticas, en orden de registro |
| Comprobación de colisión de rutas | Sí | No |
| Se aplica [Router.Prefix](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Router.Prefix.md) | Sí | No |

> [!WARNING]
> Las rutas dinámicas no se comprueban para colisiones, ya que Sisk no puede saber qué caminos coinciden. Cuando dos rutas dinámicas pueden coincidir con la misma solicitud, gana la que se definió primero. Por ejemplo, un servidor de archivos mapeado a `/` definido antes que un servidor de archivos mapeado a `/static` hace que el segundo sea inaccesible.

> [!IMPORTANT]
> En versiones anteriores a la 1.7, las rutas se probaban solo en su orden de registro, y las rutas regex podían tener precedencia sobre rutas estáticas definidas después de ellas. Si tu aplicación depende de que una ruta regex maneje un camino que también coincide con una ruta estática, mueve esa lógica a la ruta estática o estrecha el patrón de ruta estática.

### Creating routes using path patterns

Para aplicaciones nuevas, prefiere los métodos `Map*`. Mantienen visible el método HTTP en el sitio de llamada y coinciden con la API actual del `Router`. Los métodos más antiguos `SetRoute` siguen existiendo como envoltorios de compatibilidad, pero los ejemplos nuevos deben usar `Map`, `MapGet`, `MapPost`, `MapPut`, `MapDelete`, `MapPatch`, `MapAny`, `MapOptions` o `MapHead`.

```cs
// Los métodos Map* son la forma habitual de definir rutas específicas por método.
mainRouter.MapGet("/hey/<name>", (request) =>
{
    string name = request.RouteParameters["name"].GetString();
    return new HttpResponse($"Hello, {name}");
});

mainRouter.MapPost("/form", (request) =>
{
    var formData = request.GetFormContent();
    return new HttpResponse(); // 200 ok vacío
});

// Map también puede recibir una instancia de Route cuando necesitas opciones de ruta.
mainRouter.Map(Route.Get("/image.png", (request) =>
{
    var imageStream = File.OpenRead("image.png");
    
    return new HttpResponse()
    {
        // el contenido interno de StreamContent
        // el stream se libera después de enviar
        // la respuesta.
        Content = new StreamContent(imageStream)
    };
}));

// múltiples parámetros
mainRouter.MapGet("/hey/<name>/surname/<surname>", (request) =>
{
    string name = request.RouteParameters["name"].GetString();
    string surname = request.RouteParameters["surname"].GetString();

    return new HttpResponse($"Hello, {name} {surname}!");
});
```

La propiedad [RouteParameters](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.RouteParameters.md) de HttpRequest contiene toda la información sobre las variables de ruta del request recibido.

Cada camino recibido por el servidor se normaliza antes de ejecutar la prueba del patrón de ruta, siguiendo estas reglas:

- Todos los segmentos vacíos se eliminan del camino, por ejemplo: `////foo//bar` se convierte en `/foo/bar`.
- La coincidencia de rutas es **sensible a mayúsculas/minúsculas**, a menos que [Router.MatchRoutesIgnoreCase](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Router.MatchRoutesIgnoreCase.md) esté establecido en `true`.

Las propiedades [Query](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.Query.md) y [RouteParameters](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.RouteParameters.md) de [HttpRequest](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.md) devuelven un objeto [StringValueCollection](https://docs.sisk-framework.org/api/Sisk.Core.Entity.StringValueCollection.md), donde cada propiedad indexada devuelve un [StringValue](https://docs.sisk-framework.org/api/Sisk.Core.Entity.StringValue.md) no nulo, que puede usarse como una opción/monada para convertir su valor bruto en un objeto gestionado.

El ejemplo a continuación lee el parámetro de ruta "id" y obtiene un `Guid` a partir de él. Si el parámetro no es un Guid válido, se lanza una excepción, y se devuelve un error 500 al cliente si el servidor no está manejando [Router.CallbackErrorHandler](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Router.CallbackErrorHandler.md).

```cs
mainRouter.MapGet("/user/<id>", (request) =>
{
    Guid id = request.RouteParameters["id"].GetGuid();
    return new HttpResponse($"User id: {id}");
});
```

> [!NOTE]
> Los caminos ignoran su `/` final tanto en la solicitud como en la ruta, es decir, si intentas acceder a una ruta definida como `/index/page` también podrás acceder usando `/index/page/`.
>
> También puedes forzar que las URLs terminen con `/` habilitando [HttpServerConfiguration.ForceTrailingSlash](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpServerConfiguration.ForceTrailingSlash.md).

### Creating routes using class instances

También puedes definir rutas dinámicamente usando reflexión con el atributo [RouteAttribute](https://docs.sisk-framework.org/api/Sisk.Core.Routing.RouteAttribute.md). De esta forma, la instancia de una clase cuyas métodos implementen este atributo tendrá sus rutas definidas en el router objetivo.

Para que un método sea definido como ruta, debe estar marcado con un [RouteAttribute](https://docs.sisk-framework.org/api/Sisk.Core.Routing.RouteAttribute.md), como el propio atributo o un [RouteGetAttribute](https://docs.sisk-framework.org/api/Sisk.Core.Routing.RouteGetAttribute.md). El método puede ser estático, de instancia, público o privado. Usa `MapInstance` cuando quieras mapear métodos de ruta de instancia y estáticos de un objeto. Usa `MapType` cuando quieras mapear solo métodos de ruta estáticos de un tipo.

```cs {title="Controller/MyController.cs"}
public class MyController
{
    // coincidirá con GET /
    [RouteGet]
    HttpResponse Index(HttpRequest request)
    {
        HttpResponse res = new HttpResponse();
        res.Content = new StringContent("Index!");
        return res;
    }
    
    // los métodos estáticos también funcionan
    [RouteGet("/hello")]
    static HttpResponse Hello(HttpRequest request)
    {
        HttpResponse res = new HttpResponse();
        res.Content = new StringContent("Hello world!");
        return res;
    }
}
```

La línea siguiente definirá tanto los métodos `Index` como `Hello` de `MyController` como rutas, ya que ambos están marcados como rutas, y se ha proporcionado una instancia de la clase, no su tipo. Si se hubiera proporcionado su tipo en lugar de una instancia, solo se definirían los métodos estáticos.

```cs
var myController = new MyController();
mainRouter.MapInstance(myController);
```

Para mapear solo los métodos de ruta estáticos de un tipo, usa:

```cs
mainRouter.MapType<MyController>();
```

Desde la versión 0.16 de Sisk, es posible habilitar AutoScan, que buscará clases definidas por el usuario que implementen `RouterModule` y las asociará automáticamente con el router. Esto no es compatible con compilación AOT.

```cs
mainRouter.AutoScanModules<ApiController>();
```

La instrucción anterior buscará todos los tipos que implementen `ApiController` pero **no el propio tipo**. Los dos parámetros opcionales indican cómo el método buscará esos tipos. El primer argumento implica el ensamblado donde se buscarán los tipos y el segundo indica la forma en que los tipos serán definidos.

## Regex routes

En lugar de usar los métodos de coincidencia de ruta HTTP por defecto, puedes definir una ruta cuyo camino se interprete como una expresión regular con la clase [RegexRoute](https://docs.sisk-framework.org/api/Sisk.Core.Routing.RegexRoute.md). Las rutas regex son [rutas dinámicas](#matching-precedence).

```cs
mainRouter.Map(new RegexRoute(RouteMethod.Get, @"\/[a-z]+\/", request =>
{
    return new HttpResponse("hello, world");
}));
```

También puedes capturar grupos del patrón regex en el contenido de [HttpRequest.RouteParameters](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.RouteParameters.md):

```cs {title="Controller/MyController.cs"}
public class MyController
{
    [RegexRoute(RouteMethod.Get, @"/uploads/(?<filename>.*\.(jpeg|jpg|png))")]
    static HttpResponse RegexRoute(HttpRequest request)
    {
        string filename = request.RouteParameters["filename"].GetString();
        return new HttpResponse().WithContent($"Acessing file {filename}");
    }
}
```

> [!NOTE]
> La expresión regular no se ancla automáticamente: un patrón como `/uploads` también coincide con `/files/uploads/a`. Usa `^` y `$` para coincidir con todo el camino.

> [!IMPORTANT]
> Desde Sisk 1.7, la propiedad `Route.UseRegex` ya no puede establecerse. Está obsoleta y solo indica si la ruta es una [RegexRoute](https://docs.sisk-framework.org/api/Sisk.Core.Routing.RegexRoute.md). La propiedad `UseRegex` de los atributos de ruta, como `[Route(RouteMethod.Get, "...", UseRegex = true)]`, y el atributo `[RegexRoute]` siguen funcionando.

Para migrar código que creaba rutas regex con `Route.UseRegex`, reemplaza la ruta por una `RegexRoute`:

```cs
// antes de 1.7
mainRouter.Map(new Route(RouteMethod.Get, @"^/items/(?<id>\d+)$", action) { UseRegex = true });

// 1.7 y posteriores
mainRouter.Map(new RegexRoute(RouteMethod.Get, @"^/items/(?<id>\d+)$", action));
```

## Prefix routes

Una [PrefixRoute](https://docs.sisk-framework.org/api/Sisk.Core.Routing.PrefixRoute.md) coincide con su camino y con cualquier camino anidado bajo él, comparando segmentos de camino completos. Es la ruta usada por [Router.MapFileSystem](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Router.MapFileSystem.md). Las rutas prefijo son [rutas dinámicas](#matching-precedence).

```cs
// coincide con /docs, /docs/ y /docs/any/nested/path, pero no con /docsfoo
mainRouter.Map(new PrefixRoute(RouteMethod.Get, "/docs", null, request =>
{
    return new HttpResponse($"Requested {request.Path}");
}, null));
```

## Custom route matching

Puedes crear tus propias reglas de coincidencia heredando de [Route](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Route.md) y sobrescribiendo [Route.Match](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Route.Match.md). El método recibe el camino de la solicitud y el router que está realizando la coincidencia, y devuelve un [RouteMatch](https://docs.sisk-framework.org/api/Sisk.Core.Routing.RouteMatch.md) con el resultado y, opcionalmente, los parámetros de ruta capturados.

El router valida el método HTTP solo después de que `Match` tenga éxito, por lo que `Match` solo debe probar el camino. Esto mantiene funcionando las respuestas de [405 Method Not Allowed](#method-not-allowed-405-callback-handler) y `OPTIONS` para rutas personalizadas.

```cs {title="Routing/ExtensionRoute.cs"}
// coincide con cualquier camino que termine con la extensión dada, como /reports/2024.csv
public sealed class ExtensionRoute : Route
{
    public ExtensionRoute(string extension, RouteAction action)
        : base(RouteMethod.Get, extension, action)
    {
    }

    // esta ruta no sigue un patrón de ruta
    public override bool IsStatic => false;

    // el camino no es un patrón de ruta, por lo que no puede redirigirse a su forma con barra final
    public override bool AllowRewrites => false;

    public override RouteMatch Match(string requestPath, Router router)
    {
        StringComparison comparison = router.MatchRoutesIgnoreCase
            ? StringComparison.OrdinalIgnoreCase
            : StringComparison.Ordinal;

        if (!requestPath.EndsWith(Path, comparison))
            return RouteMatch.NotMatched;

        var parameters = new NameValueCollection
        {
            ["file"] = requestPath[..^Path.Length]
        };
        return new RouteMatch(true, parameters);
    }
}
```

```cs
mainRouter.Map(new ExtensionRoute(".csv", request =>
{
    string file = request.RouteParameters["file"].GetString();
    return new HttpResponse($"Exporting {file} as CSV");
}));
```

Al sobrescribir `Match`, ten en cuenta:

- Sobrescribe [Route.IsStatic](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Route.IsStatic.md) para devolver `false` siempre que la ruta no siga estrictamente un patrón de ruta. De lo contrario, el router trata su `Path` como un patrón de ruta al comprobar colisiones y aplicar [Router.Prefix](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Router.Prefix.md), lo que puede producir colisiones falsas o caminos inválidos.
- Sobrescribe [Route.AllowRewrites](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Route.AllowRewrites.md) para devolver `false` cuando el camino de la ruta no pueda redirigirse a su forma con barra final mediante [HttpServerConfiguration.ForceTrailingSlash](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpServerConfiguration.ForceTrailingSlash.md).
- `Match` se llama para cada solicitud que llega a la ruta, posiblemente desde varios hilos simultáneamente. Evita asignaciones cuando la ruta no coincide y mantén cualquier estado en caché seguro para hilos. Devuelve [RouteMatch.NotMatched](https://docs.sisk-framework.org/api/Sisk.Core.Routing.RouteMatch.NotMatched.md) para coincidencias fallidas.
- Respeta [Router.MatchRoutesIgnoreCase](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Router.MatchRoutesIgnoreCase.md) a través del parámetro `router`.

## Prefixing routes

Puedes anteponer todas las rutas en una clase o módulo con el atributo [RoutePrefix](https://docs.sisk-framework.org/api/Sisk.Core.Routing.RoutePrefixAttribute.md) y establecer el prefijo como una cadena.

Mira el ejemplo siguiente usando la arquitectura BREAD (Browse, Read, Edit, Add and Delete):

```cs {title="Controller/Api/UsersController.cs"}
[RoutePrefix("/api/users")]
public class UsersController
{
    // GET /api/users
    [RouteGet]
    public async Task<HttpResponse> Browse()
    {
        ...
    }
    
    // GET /api/users/<id>
    [RouteGet("/<id>")]
    public async Task<HttpResponse> Read()
    {
        ...
    }
    
    // PATCH /api/users/<id>
    [RoutePatch("/<id>")]
    public async Task<HttpResponse> Edit()
    {
        ...
    }
    
    // POST /api/users
    [RoutePost]
    public async Task<HttpResponse> Add()
    {
        ...
    }
    
    // DELETE /api/users/<id>
    [RouteDelete("/<id>")]
    public async Task<HttpResponse> Delete()
    {
        ...
    }
}
```

En el ejemplo anterior, el parámetro HttpResponse se omite a favor de usarlo a través del contexto global [HttpContext.Current](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpContext.Current.md). Lee más en la sección que sigue.

## Routes without request parameter

Las rutas pueden definirse sin el parámetro [HttpRequest](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.md) y aún así es posible obtener la solicitud y sus componentes en el contexto de la solicitud. Consideremos una abstracción `ControllerBase` que sirve como base para todos los controladores de una API, y que dicha abstracción proporciona la propiedad `Request` para obtener el [HttpRequest](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.md) actual.

```cs {title="Controller/ControllerBase.cs"}
public abstract class ControllerBase
{
    // obtiene la solicitud del hilo actual
    public HttpRequest Request { get => HttpContext.Current.Request; }
    
    // la línea siguiente, cuando se llama, obtiene la base de datos de la sesión HTTP actual,
    // o crea una nueva si no existe
    public DbContext Database { get => HttpContext.Current.RequestBag.GetOrAdd<DbContext>(); }
}
```

Y para que todos sus descendientes puedan usar la sintaxis de ruta sin el parámetro de solicitud:

```cs {title="Controller/UsersController.cs"}
[RoutePrefix("/api/users")]
public class UsersController : ControllerBase
{    
    [RoutePost]
    public async Task<HttpResponse> Create()
    {
        // lee los datos JSON de la solicitud actual
        UserCreationDto? user = await Request.GetJsonContentAsync<UserCreationDto>();
        ...
        Database.Users.Add(user);
        
        return new HttpResponse(201);
    }
}
```

Más detalles sobre el contexto actual y la inyección de dependencias pueden encontrarse en el tutorial de [dependency injection](https://docs.sisk-framework.org/es/docs/features/instancing.md).

## Any method routes

Puedes definir una ruta que se coincida solo por su camino y omitir el método HTTP. Esto puede ser útil para validar el método dentro del callback de la ruta.

```cs
// coincidirá con / en cualquier método HTTP
mainRouter.MapAny("/", callbackFunction);
```

[RouteMethod.Any](https://docs.sisk-framework.org/api/Sisk.Core.Routing.RouteMethod.md) incluye `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `QUERY`, `HEAD` y `OPTIONS`.

## QUERY method routes

Sisk soporta el método HTTP `QUERY`, un método seguro e idempotente que lleva la consulta en el cuerpo de la solicitud. Usa [Router.MapQuery](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Router.MapQuery.md), [Route.Query](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Route.Query.md), [RouteMethod.Query](https://docs.sisk-framework.org/api/Sisk.Core.Routing.RouteMethod.md) o el atributo [RouteQuery](https://docs.sisk-framework.org/api/Sisk.Core.Routing.RouteQueryAttribute.md) para definir estas rutas:

```cs
mainRouter.MapQuery("/search", request =>
{
    string filter = request.Body;
    return new HttpResponse($"Searching for {filter}");
});
```

```cs {title="Controller/SearchController.cs"}
public class SearchController
{
    [RouteQuery("/products")]
    static HttpResponse SearchProducts(HttpRequest request)
    {
        return new HttpResponse($"Searching for {request.Body}");
    }
}
```

El encabezado `Accept-Query`, que anuncia los tipos de medio aceptados en solicitudes `QUERY`, está disponible a través de [HttpHeaderCollection.AcceptQuery](https://docs.sisk-framework.org/api/Sisk.Core.Entity.HttpHeaderCollection.AcceptQuery.md) y [HttpKnownHeaderNames.AcceptQuery](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpKnownHeaderNames.AcceptQuery.md).

## Any path routes

Las rutas de cualquier camino prueban cualquier camino recibido por el servidor HTTP, sujeto al método de la ruta que se está probando. Si el método de la ruta es RouteMethod.Any y la ruta usa [Route.AnyPath](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Route.AnyPath.md) en su expresión de camino, esta ruta escuchará todas las solicitudes del servidor HTTP, y no se podrán definir otras rutas.

```cs
// la siguiente ruta coincidirá con todas las solicitudes POST
mainRouter.Map(RouteMethod.Post, Route.AnyPath, callbackFunction);
```

## Ignore case route matching

Por defecto, la interpretación de rutas con solicitudes es sensible a mayúsculas/minúsculas. Para hacer que ignore mayúsculas, habilita esta opción:

```cs
mainRouter.MatchRoutesIgnoreCase = true;
```

Esto también habilitará la opción `RegexOptions.IgnoreCase` para rutas regex y la comparación sin distinción de mayúsculas para rutas prefijo.

## Not Found (404) callback handler

Puedes crear un callback personalizado para cuando una solicitud no coincida con ninguna ruta conocida.

```cs
mainRouter.NotFoundErrorHandler = () =>
{
    return new HttpResponse(404)
    {
        // Desde v0.14
        Content = new HtmlContent("<h1>Not found</h1>")
        // versiones anteriores
        Content = new StringContent("<h1>Not found</h1>", Encoding.UTF8, "text/html")
    };
};
```

## Method not allowed (405) callback handler

También puedes crear un callback personalizado para cuando una solicitud coincida con su camino, pero no con el método.

```cs
mainRouter.MethodNotAllowedErrorHandler = (context) =>
{
    return new HttpResponse(405)
    {
        Content = new StringContent($"Method not allowed for this route.")
    };
};
```

## Error Handling

Las excepciones pueden lanzarse dentro del ciclo de vida de una solicitud, que abarca desde el manejador de solicitud previo a la ejecución, pasando por la acción del router, hasta los manejadores de solicitud posteriores y los manejadores de valores. Estas excepciones son gestionadas por el mecanismo:

- Si [HttpServerConfiguration.ThrowExceptions](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpServerConfiguration.ThrowExceptions.md) es `true`, las excepciones se lanzarán normalmente y no serán capturadas por Sisk, y el servidor HTTP puede interrumpirse si la excepción no se captura.
- Si [HttpServerConfiguration.ThrowExceptions](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpServerConfiguration.ThrowExceptions.md) es `false`, las excepciones serán capturadas y manejadas por Sisk. Después de eso, si `Router.CallbackErrorHandler` está definido, se llamará con la excepción capturada y el contexto de la solicitud, y **no** se reenviará a la salida de error estándar. Si `Router.CallbackErrorHandler` no está definido, la excepción se reenviará a la salida de error estándar, y el cliente recibirá una respuesta HTTP 500. Si la salida de error estándar no está definida, el error se ignorará silenciosamente.

Nota: dentro de `Router.CallbackErrorHandler`, puedes establecer el modo de registro para errores, registro de acceso, ambos o ninguno, y alterar el comportamiento predeterminado de escritura de logs:

```csharp
router.CallbackErrorHandler = (ex, ctx) =>
{
    ctx.LogMode = LogOutput.Both; // sobrescribe el modo de registro para registrar el error tanto en el log de acceso como en el de errores
}
```

## Internal error handler

Los callbacks de ruta pueden lanzar errores durante la ejecución del servidor. Si no se manejan correctamente, el funcionamiento general del servidor HTTP puede terminar. El router tiene un callback para cuando un callback de ruta falla y evita la interrupción del servicio.

Este método solo es accesible cuando [ThrowExceptions](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpServerConfiguration.ThrowExceptions.md) está configurado en false.

```cs
mainRouter.CallbackErrorHandler = (ex, context) =>
{
    return new HttpResponse(500)
    {
        Content = new StringContent($"Error: {ex.Message}")
    };
};
```
