# Roteamento

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

O [Router](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Router.md) é o primeiro passo na construção do servidor. Ele é responsável por armazenar objetos [Route](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Route.md), que são pontos de extremidade que mapeiam URLs e seus métodos para ações executadas pelo servidor. Cada ação é responsável por receber uma requisição e entregar uma resposta ao cliente.

As rotas são pares de expressões de caminho (“padrão de caminho”) e o método HTTP que podem escutar. Quando uma requisição é feita ao servidor, ele tentará encontrar uma rota que corresponda à requisição recebida, então chamará a ação dessa rota e entregará a resposta resultante ao cliente.

Existem várias maneiras de definir rotas no Sisk: elas podem ser estáticas, dinâmicas ou auto‑escanadas, definidas por atributos, ou diretamente no objeto Router.

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

// mapeia a rota GET / para a ação a seguir
mainRouter.MapGet("/", request => {
    return new HttpResponse("Hello, world!");
});
```

Para entender o que uma rota pode fazer, precisamos entender o que uma requisição pode fazer. Um [HttpRequest](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.md) conterá tudo o que você precisa. O Sisk também inclui alguns recursos extras que aceleram o desenvolvimento geral.

Para cada ação recebida pelo servidor, um delegate do tipo [RouteAction](https://docs.sisk-framework.org/api/Sisk.Core.Routing.RouteAction.md) será chamado. Esse delegate contém um parâmetro que mantém um [HttpRequest](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.md) com todas as informações necessárias sobre a requisição recebida pelo servidor. O objeto resultante desse delegate deve ser um [HttpResponse](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpResponse.md) ou um objeto que mapeie para ele através de [implicit response types](https://docs.sisk-framework.org/pt-br/docs/fundamentals/responses.md#implicit-response-types).

## Correspondência de rotas

Quando uma requisição é recebida pelo servidor HTTP, o Sisk procura uma rota que satisfaça a expressão do caminho recebido pela requisição. A expressão é sempre testada entre a rota e o caminho da requisição, sem considerar a string de consulta.

A primeira rota cujo caminho e método correspondam à requisição a trata. Quando nenhuma rota corresponde a essa requisição, a resposta de [Router.NotFoundErrorHandler](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Router.NotFoundErrorHandler.md) é retornada ao cliente. Quando o padrão de caminho corresponde, mas o método HTTP não, a resposta de [Router.MethodNotAllowedErrorHandler](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Router.MethodNotAllowedErrorHandler.md) é enviada de volta ao cliente. A ordem em que as rotas são testadas é descrita em [precedência de correspondência](#matching-precedence).

O Sisk verifica a possibilidade de colisões de rotas entre rotas estáticas. Quando o servidor inicia, o Sisk procura rotas estáticas cujos padrões de caminho e métodos se sobrepõem, e lança uma exceção se alguma for encontrada. Na mesma verificação, o Sisk lança um `ArgumentException` se duas ou mais rotas compartilharem o mesmo [Route.Name](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Route.Name.md). Ambas as verificações são controladas por [Router.CheckForRouteCollisions](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Router.CheckForRouteCollisions.md), que está habilitado por padrão.

### Precedência de correspondência

Cada rota é **estática** ou **dinâmica**, conforme indicado por [Route.IsStatic](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Route.IsStatic.md):

- **Rotas estáticas** seguem estritamente seu padrão de caminho, como `/users/<id>`. Rotas criadas com métodos `Map*`, instâncias `Route` e atributos de rota são estáticas.
- **Rotas dinâmicas** interpretam seu caminho à sua maneira e podem corresponder a caminhos que não podem ser descritos por um padrão de caminho. [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/pt-br/docs/features/file-server.md)) e [custom matching routes](#custom-route-matching) são dinâmicas.

Quando o servidor inicia, o router reordena suas rotas uma única vez de modo que toda rota estática seja testada antes de toda rota dinâmica. Dentro de cada grupo, as rotas mantêm a ordem de registro. Isso significa que uma rota estática sempre tem precedência sobre uma rota dinâmica, independentemente da ordem em que foram definidas:

```cs
// dinâmico: serve qualquer caminho GET do diretório "wwwroot"
mainRouter.MapFileSystem("/", "wwwroot");

// estático: trata GET /api/status, mesmo que tenha sido definido após o servidor de arquivos
mainRouter.MapGet("/api/status", request => new HttpResponse("ok"));
```

No exemplo acima, o servidor de arquivos atua como fallback para qualquer requisição GET que não seja tratada por uma rota estática.

Os dois grupos também diferem em como são validados e transformados:

| | Rotas estáticas | Rotas dinâmicas |
|---|---|---|
| Ordem de correspondência | Primeira, na ordem de registro | Após todas as rotas estáticas, na ordem de registro |
| Checagem de colisão de rotas | Sim | Não |
| [Router.Prefix](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Router.Prefix.md) é aplicado | Sim | Não |

> [!WARNING]
> Rotas dinâmicas não são verificadas quanto a colisões, pois o Sisk não pode saber quais caminhos elas correspondem. Quando duas rotas dinâmicas podem corresponder à mesma requisição, a que foi definida primeiro vence. Por exemplo, um servidor de arquivos mapeado para `/` definido antes de um servidor de arquivos mapeado para `/static` torna o segundo inacessível.

> [!IMPORTANT]
> Em versões anteriores à 1.7, as rotas eram testadas apenas na ordem de registro, e rotas regex podiam ter precedência sobre rotas estáticas definidas depois delas. Se sua aplicação depende de uma rota regex que trata um caminho também correspondido por uma rota estática, mova essa lógica para a rota estática ou restrinja o padrão de caminho estático.

### Criando rotas usando padrões de caminho

Para novas aplicações, prefira os métodos `Map*`. Eles mantêm o método HTTP visível no ponto de chamada e correspondem à API atual do `Router`. Os métodos mais antigos `SetRoute` ainda existem como wrappers de compatibilidade, mas novos exemplos devem usar `Map`, `MapGet`, `MapPost`, `MapPut`, `MapDelete`, `MapPatch`, `MapAny`, `MapOptions` ou `MapHead`.

```cs
// Métodos Map* são a forma usual de definir rotas 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 vazio
});

// Map também pode receber uma instância de Route quando você precisa de opções de rota.
mainRouter.Map(Route.Get("/image.png", (request) =>
{
    var imageStream = File.OpenRead("image.png");
    
    return new HttpResponse()
    {
        // o interior do StreamContent
        // o stream é descartado após o envio
        // da resposta.
        Content = new StreamContent(imageStream)
    };
}));

// múltiplos 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}!");
});
```

A propriedade [RouteParameters](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.RouteParameters.md) de `HttpRequest` contém todas as informações sobre as variáveis de caminho da requisição recebida.

Todo caminho recebido pelo servidor é normalizado antes da execução do teste de padrão de caminho, seguindo estas regras:

- Todos os segmentos vazios são removidos do caminho, por exemplo: `////foo//bar` torna‑se `/foo/bar`.
- A correspondência de caminho é **sensível a maiúsculas/minúsculas**, a menos que [Router.MatchRoutesIgnoreCase](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Router.MatchRoutesIgnoreCase.md) esteja definido como `true`.

As propriedades [Query](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.Query.md) e [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) retornam um objeto [StringValueCollection](https://docs.sisk-framework.org/api/Sisk.Core.Entity.StringValueCollection.md), onde cada propriedade indexada devolve um [StringValue](https://docs.sisk-framework.org/api/Sisk.Core.Entity.StringValue.md) não nulo, que pode ser usado como uma opção/monad para converter seu valor bruto em um objeto gerenciado.

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

> [!NOTE]
> Caminhos têm sua barra final `/` ignorada tanto na requisição quanto no caminho da rota; ou seja, se você tentar acessar uma rota definida como `/index/page` também poderá acessá‑la usando `/index/page/`.
>
> Você também pode forçar URLs a terminarem com `/` habilitando [HttpServerConfiguration.ForceTrailingSlash](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpServerConfiguration.ForceTrailingSlash.md).

### Criando rotas usando instâncias de classe

Você também pode definir rotas dinamicamente usando reflexão com o atributo [RouteAttribute](https://docs.sisk-framework.org/api/Sisk.Core.Routing.RouteAttribute.md). Dessa forma, a instância de uma classe cujos métodos implementam esse atributo terá suas rotas definidas no router de destino.

Para que um método seja definido como rota, ele deve ser marcado com um [RouteAttribute](https://docs.sisk-framework.org/api/Sisk.Core.Routing.RouteAttribute.md), como o próprio atributo ou um [RouteGetAttribute](https://docs.sisk-framework.org/api/Sisk.Core.Routing.RouteGetAttribute.md). O método pode ser estático, de instância, público ou privado. Use `MapInstance` quando quiser mapear métodos de rota de instância e estáticos de um objeto. Use `MapType` quando quiser mapear apenas métodos de rota estáticos de um tipo.

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

A linha abaixo definirá tanto os métodos `Index` quanto `Hello` de `MyController` como rotas, pois ambos estão marcados como rotas, e uma instância da classe foi fornecida, não seu tipo. Se seu tipo tivesse sido fornecido em vez de uma instância, apenas os métodos estáticos seriam definidos.

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

Para mapear apenas métodos de rota estáticos de um tipo, use:

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

Desde a versão 0.16 do Sisk, é possível habilitar AutoScan, que buscará classes definidas pelo usuário que implementam `RouterModule` e as associará automaticamente ao router. Isso não é suportado com compilação AOT.

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

A instrução acima buscará todos os tipos que implementam `ApiController`, mas **não o próprio tipo**. Os dois parâmetros opcionais indicam como o método buscará esses tipos. O primeiro argumento indica o Assembly onde os tipos serão buscados e o segundo indica a forma como os tipos serão definidos.

## Rotas Regex

Em vez de usar os métodos padrão de correspondência de caminho HTTP, você pode definir uma rota cujo caminho é interpretado como expressão regular com a classe [RegexRoute](https://docs.sisk-framework.org/api/Sisk.Core.Routing.RegexRoute.md). Rotas regex são [rotas dinâmicas](#matching-precedence).

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

Você também pode capturar grupos do padrão regex dentro do conteúdo 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]
> A expressão regular não é ancorada automaticamente: um padrão como `/uploads` também corresponde a `/files/uploads/a`. Use `^` e `$` para corresponder ao caminho inteiro.

> [!IMPORTANT]
> Desde o Sisk 1.7, a propriedade `Route.UseRegex` não pode mais ser definida. Ela está obsoleta e apenas indica se a rota é uma [RegexRoute](https://docs.sisk-framework.org/api/Sisk.Core.Routing.RegexRoute.md). A propriedade `UseRegex` de atributos de rota, como `[Route(RouteMethod.Get, "...", UseRegex = true)]`, e o atributo `[RegexRoute]` continuam funcionando.

Para migrar código que criava rotas regex com `Route.UseRegex`, substitua a rota por um `RegexRoute`:

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

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

## Rotas Prefix

Uma [PrefixRoute](https://docs.sisk-framework.org/api/Sisk.Core.Routing.PrefixRoute.md) corresponde ao seu caminho e a todos os caminhos aninhados sob ele, comparando segmentos de caminho completos. É a rota usada por [Router.MapFileSystem](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Router.MapFileSystem.md). Rotas prefix são [rotas dinâmicas](#matching-precedence).

```cs
// corresponde a /docs, /docs/ e /docs/any/nested/path, mas não a /docsfoo
mainRouter.Map(new PrefixRoute(RouteMethod.Get, "/docs", null, request =>
{
    return new HttpResponse($"Requested {request.Path}");
}, null));
```

## Correspondência de rotas personalizada

Você pode criar suas próprias regras de correspondência herdando [Route](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Route.md) e sobrescrevendo [Route.Match](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Route.Match.md). O método recebe o caminho da requisição e o router que está correspondendo a rota, e retorna um [RouteMatch](https://docs.sisk-framework.org/api/Sisk.Core.Routing.RouteMatch.md) com o resultado e, opcionalmente, os parâmetros de rota capturados.

O router valida o método HTTP somente após o `Match` ter sucesso, portanto `Match` deve testar apenas o caminho. Isso mantém as respostas [405 Method Not Allowed](#method-not-allowed-405-callback-handler) e `OPTIONS` funcionando para rotas personalizadas.

```cs {title="Routing/ExtensionRoute.cs"}
 // corresponde a qualquer caminho que termina com a extensão de arquivo fornecida, como /reports/2024.csv
public sealed class ExtensionRoute : Route
{
    public ExtensionRoute(string extension, RouteAction action)
        : base(RouteMethod.Get, extension, action)
    {
    }

    // esta rota não segue um padrão de caminho
    public override bool IsStatic => false;

    // o caminho não é um padrão de caminho, portanto não pode ser redirecionado para sua forma com 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");
}));
```

Ao sobrescrever `Match`, tenha em mente:

- Sobrescreva [Route.IsStatic](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Route.IsStatic.md) para retornar `false` sempre que a rota não seguir estritamente um padrão de caminho. Caso contrário, o router trata seu `Path` como um padrão de caminho ao verificar colisões e ao aplicar [Router.Prefix](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Router.Prefix.md), o que pode gerar colisões falsas ou caminhos inválidos.
- Sobrescreva [Route.AllowRewrites](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Route.AllowRewrites.md) para retornar `false` quando o caminho da rota não puder ser redirecionado para sua forma com barra final por [HttpServerConfiguration.ForceTrailingSlash](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpServerConfiguration.ForceTrailingSlash.md).
- `Match` é chamado para cada requisição que chega à rota, possivelmente de múltiplas threads simultaneamente. Evite alocações quando a rota não corresponder e mantenha qualquer estado em cache thread‑safe. Retorne [RouteMatch.NotMatched](https://docs.sisk-framework.org/api/Sisk.Core.Routing.RouteMatch.NotMatched.md) para correspondências malsucedidas.
- Respeite [Router.MatchRoutesIgnoreCase](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Router.MatchRoutesIgnoreCase.md) através do parâmetro `router`.

## Prefixando rotas

Você pode prefixar todas as rotas em uma classe ou módulo com o atributo [RoutePrefix](https://docs.sisk-framework.org/api/Sisk.Core.Routing.RoutePrefixAttribute.md) e definir o prefixo como uma string.

Veja o exemplo abaixo usando a arquitetura 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()
    {
        ...
    }
}
```

No exemplo acima, o parâmetro `HttpResponse` é omitido em favor de ser usado através do contexto global [HttpContext.Current](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpContext.Current.md). Leia mais na seção que se segue.

## Rotas sem parâmetro de requisição

Rotas podem ser definidas sem o parâmetro [HttpRequest](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.md) e ainda assim ser possível obter a requisição e seus componentes no contexto da requisição. Vamos considerar uma abstração `ControllerBase` que serve como base para todos os controladores de uma API, e que fornece a propriedade `Request` para obter o [HttpRequest](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.md) atual.

```cs {title="Controller/ControllerBase.cs"}
public abstract class ControllerBase
{
    // obtém a requisição da thread atual
    public HttpRequest Request { get => HttpContext.Current.Request; }
    
    // a linha abaixo, quando chamada, obtém o banco de dados da sessão HTTP atual,
    // ou cria um novo se não existir
    public DbContext Database { get => HttpContext.Current.RequestBag.GetOrAdd<DbContext>(); }
}
```

E para que todos os seus descendentes possam usar a sintaxe de rota sem o parâmetro de requisição:

```cs {title="Controller/UsersController.cs"}
[RoutePrefix("/api/users")]
public class UsersController : ControllerBase
{    
    [RoutePost]
    public async Task<HttpResponse> Create()
    {
        // lê os dados JSON da requisição atual
        UserCreationDto? user = await Request.GetJsonContentAsync<UserCreationDto>();
        ...
        Database.Users.Add(user);
        
        return new HttpResponse(201);
    }
}
```

Mais detalhes sobre o contexto atual e injeção de dependência podem ser encontrados no tutorial de [dependency injection](https://docs.sisk-framework.org/pt-br/docs/features/instancing.md).

## Rotas de qualquer método

Você pode definir uma rota para ser correspondida apenas pelo seu caminho e ignorar o método HTTP. Isso pode ser útil para validar o método dentro do callback da rota.

```cs
// corresponderá a / em qualquer método HTTP
mainRouter.MapAny("/", callbackFunction);
```

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

## Rotas do método QUERY

O Sisk suporta o método HTTP `QUERY`, um método seguro e idempotente que transporta a consulta no corpo da requisição. Use [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) ou o atributo [RouteQuery](https://docs.sisk-framework.org/api/Sisk.Core.Routing.RouteQueryAttribute.md) para definir essas rotas:

```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}");
    }
}
```

O cabeçalho `Accept-Query`, que anuncia os tipos de mídia aceitos em requisições `QUERY`, está disponível através de [HttpHeaderCollection.AcceptQuery](https://docs.sisk-framework.org/api/Sisk.Core.Entity.HttpHeaderCollection.AcceptQuery.md) e [HttpKnownHeaderNames.AcceptQuery](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpKnownHeaderNames.AcceptQuery.md).

## Rotas de qualquer caminho

Rotas de qualquer caminho testam qualquer caminho recebido pelo servidor HTTP, sujeito ao método da rota ser testado. Se o método da rota for `RouteMethod.Any` e a rota usar [Route.AnyPath](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Route.AnyPath.md) em sua expressão de caminho, essa rota ouvirá todas as requisições do servidor HTTP, e nenhuma outra rota poderá ser definida.

```cs
// a rota a seguir corresponderá a todas as requisições POST
mainRouter.Map(RouteMethod.Post, Route.AnyPath, callbackFunction);
```

## Ignorando diferenciação de maiúsculas/minúsculas na correspondência de rotas

Por padrão, a interpretação das rotas com as requisições diferencia maiúsculas de minúsculas. Para fazer com que ignore isso, habilite esta opção:

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

Isso também habilitará a opção `RegexOptions.IgnoreCase` para rotas regex e comparação sem diferenciação de maiúsculas/minúsculas para rotas prefix.

## Manipulador de callback Not Found (404)

Você pode criar um callback customizado para quando uma requisição não corresponder a nenhuma rota conhecida.

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

## Manipulador de callback Method not allowed (405)

Você também pode criar um callback customizado para quando uma requisição corresponde ao seu caminho, mas não ao método.

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

## Tratamento de Erros

Exceções podem ser lançadas dentro do ciclo de vida de uma requisição, que vai do manipulador de requisição pré‑execução, passando pela ação do router, até os manipuladores de requisição pós‑execução e manipuladores de valor. Essas exceções são gerenciadas pelo mecanismo:

- Se [HttpServerConfiguration.ThrowExceptions](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpServerConfiguration.ThrowExceptions.md) for `true`, as exceções serão lançadas normalmente e não serão capturadas pelo Sisk, e o servidor HTTP pode ser interrompido se a exceção não for tratada.
- Se [HttpServerConfiguration.ThrowExceptions](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpServerConfiguration.ThrowExceptions.md) for `false`, as exceções serão capturadas e tratadas pelo Sisk. Depois disso, se `Router.CallbackErrorHandler` estiver definido, ele será chamado com a exceção capturada e o contexto da requisição, e **não** será encaminhado para a saída de erro padrão. Se `Router.CallbackErrorHandler` não estiver definido, a exceção será encaminhada para a saída de erro padrão, e o cliente receberá uma resposta HTTP 500. Se a saída de erro padrão não estiver definida, o erro será ignorado silenciosamente.

Nota: dentro de `Router.CallbackErrorHandler`, você pode definir o modo de log para erros, log de acesso, ambos ou nenhum, e alterar o comportamento padrão de escrita de logs:

```csharp
router.CallbackErrorHandler = (ex, ctx) =>
{
    ctx.LogMode = LogOutput.Both; // sobrescreve o modo de log para registrar o erro tanto no log de acesso quanto no de erro
}
```

## Manipulador interno de erro

Callbacks de rotas podem lançar erros durante a execução do servidor. Se não forem tratados corretamente, o funcionamento geral do servidor HTTP pode ser interrompido. O router possui um callback para quando um callback de rota falha e impede a interrupção do serviço.

Esse método só é acessível quando [ThrowExceptions](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpServerConfiguration.ThrowExceptions.md) está definido como false.

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