# Маршрутизация

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

The [Router](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Router.md) is the first step in building the server. It is responsible for housing [Route](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Route.md) objects, which are endpoints that map URLs and their methods to actions executed by the server. Each action is responsible for receiving a request and delivering a response to the client.

The routes are pairs of path expressions ("path pattern") and the HTTP method that they can listen to. When a request is made to the server, it will attempt to find a route that matches the received request, then it will call the action of that route and deliver the resulting response to the client.

There are multiple ways to define routes in Sisk: they can be static, dynamic or auto-scanned, defined by attributes, or directly in the Router object.

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

// maps the GET / route into the following action
// отображает маршрут GET / в следующее действие
mainRouter.MapGet("/", request => {
    return new HttpResponse("Hello, world!");
});
```

To understand what a route is capable of doing, we need to understand what a request is capable of doing. An [HttpRequest](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.md) will contain everything you need. Sisk also includes some extra features that speed up the overral development.

For every action received by the server, a delegate of type [RouteAction](https://docs.sisk-framework.org/api/Sisk.Core.Routing.RouteAction.md) will be called. This delegate contains an parameter holding an [HttpRequest](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.md) with all the necessary information about the request received by the server. The resulting object from this delegate must be an [HttpResponse](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpResponse.md) or an object that maps to it through [implicit response types](https://docs.sisk-framework.org/ru/docs/fundamentals/responses.md#implicit-response-types).

## Сопоставление маршрутов

When a request is received by the HTTP server, Sisk searches for a route that satisfies the expression of the path received by the request. The expression is always tested between the route and the request path, without considering the query string.

The first route whose path and method match the request handles it. When no route is matched with that request, the [Router.NotFoundErrorHandler](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Router.NotFoundErrorHandler.md) response is returned to the client. When the path pattern is matched, but the HTTP method is mismatched, the [Router.MethodNotAllowedErrorHandler](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Router.MethodNotAllowedErrorHandler.md) response is sent back to the client. The order in which routes are tested is described in [matching precedence](#matching-precedence).

Sisk checks for the possibility of route collisions between static routes. When the server starts, Sisk will look for static routes whose path patterns and methods overlap, and throws an exception if any is found. In the same check, Sisk throws an `ArgumentException` if two or more routes share the same [Route.Name](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Route.Name.md). Both checks are controlled by [Router.CheckForRouteCollisions](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Router.CheckForRouteCollisions.md), which is enabled by default.

### Приоритет сопоставления

Every route is either **static** or **dynamic**, as indicated by [Route.IsStatic](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Route.IsStatic.md):

- **Static routes** strictly follow their path pattern, such as `/users/<id>`. Routes created with `Map*` methods, `Route` instances and route attributes are static.
- **Dynamic routes** interpret their path in their own way and may match paths that cannot be described by a path pattern. [RegexRoute](https://docs.sisk-framework.org/api/Sisk.Core.Routing.RegexRoute.md), [PrefixRoute](https://docs.sisk-framework.org/api/Sisk.Core.Routing.PrefixRoute.md) (used by [file servers](https://docs.sisk-framework.org/ru/docs/features/file-server.md)) and [custom matching routes](#custom-route-matching) are dynamic.

When the server starts, the router reorders its routes once so that every static route is tested before every dynamic route. Within each group, routes keep their registration order. This means that a static route always takes precedence over a dynamic route, regardless of the order in which they were defined:

```cs
// dynamic: serves any GET path from the "wwwroot" directory
// динамический: обслуживает любой GET‑путь из каталога "wwwroot"
mainRouter.MapFileSystem("/", "wwwroot");

// static: handles GET /api/status, even though it was defined after the file server
// статический: обрабатывает GET /api/status, хотя был определён после файлового сервера
mainRouter.MapGet("/api/status", request => new HttpResponse("ok"));
```

In the example above, the file server acts as a fallback for any GET request that is not handled by a static route.

The two groups also differ in how they are validated and transformed:

| | Static routes | Dynamic routes |
|---|---|---|
| Порядок сопоставления | Сначала, в порядке регистрации | После всех статических маршрутов, в порядке регистрации |
| Проверка столкновений маршрутов | Да | Нет |
| Применяется [Router.Prefix](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Router.Prefix.md) | Да | Нет |

> [!WARNING]
> Dynamic routes are not checked for collisions, since Sisk cannot know which paths they match. When two dynamic routes can match the same request, the one defined first wins. For example, a file server mapped to `/` defined before a file server mapped to `/static` makes the second one unreachable.
>
> Динамические маршруты не проверяются на столкновения, поскольку Sisk не может знать, какие пути они соответствуют. Если два динамических маршрута могут соответствовать одному запросу, выигрывает тот, который определён первым. Например, файловый сервер, смонтированный на `/`, определённый до файлового сервера, смонтированного на `/static`, делает второй недоступным.

> [!IMPORTANT]
> In versions before 1.7, routes were tested only in their registration order, and regex routes could take precedence over static routes defined after them. If your application relies on a regex route handling a path that is also matched by a static route, move that logic into the static route or narrow the static path pattern.
>
> В версиях до 1.7 маршруты проверялись только в порядке регистрации, и маршруты‑регулярки могли иметь приоритет над статическими маршрутами, определёнными после них. Если ваше приложение зависит от маршрута‑регулярки, обрабатывающего путь, который также совпадает со статическим маршрутом, перенесите эту логику в статический маршрут или сузьте шаблон статического пути.

### Создание маршрутов с использованием шаблонов пути

For new applications, prefer the `Map*` methods. They keep the HTTP method visible at the call site and match the current `Router` API. The older `SetRoute` methods still exist as compatibility wrappers, but new examples should use `Map`, `MapGet`, `MapPost`, `MapPut`, `MapDelete`, `MapPatch`, `MapAny`, `MapOptions`, or `MapHead`.

```cs
// Map* methods are the usual way to define method-specific routes.
// Методы Map* — обычный способ определения маршрутов, специфичных для метода.
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(); // empty 200 ok
});

// Map can also receive a Route instance when you need route options.
mainRouter.Map(Route.Get("/image.png", (request) =>
{
    var imageStream = File.OpenRead("image.png");
    
    return new HttpResponse()
    {
        // the StreamContent inner
        // stream is disposed after sending
        // the response.
        // внутренний StreamContent
        // поток освобождается после отправки
        // ответа.
        Content = new StreamContent(imageStream)
    };
}));

// multiple parameters
// несколько параметров
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}!");
});
```

The [RouteParameters](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.RouteParameters.md) property of HttpRequest contains all the information about the path variables of the received request.

Every path received by the server is normalized before the path pattern test is executed, following these rules:

- All empty segments are removed from the path, eg: `////foo//bar` becomes `/foo/bar`.
  // Все пустые сегменты удаляются из пути, например: `////foo//bar` становится `/foo/bar`.
- Path matching is **case-sensitive**, unless [Router.MatchRoutesIgnoreCase](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Router.MatchRoutesIgnoreCase.md) is set to `true`.
  // Сопоставление пути **чувствительно к регистру**, если только [Router.MatchRoutesIgnoreCase] не установлен в `true`.

The [Query](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.Query.md) and [RouteParameters](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.RouteParameters.md) properties of [HttpRequest](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.md) return a [StringValueCollection](https://docs.sisk-framework.org/api/Sisk.Core.Entity.StringValueCollection.md) object, where each indexed property returns a non-null [StringValue](https://docs.sisk-framework.org/api/Sisk.Core.Entity.StringValue.md), which can be used as an option/monad to convert its raw value into a managed object.

The example below reads the route parameter "id" and obtains a `Guid` from it. If the parameter is not a valid Guid, an exception is thrown, and a 500 error is returned to the client if the server is not handling [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]
> Paths have their trailing `/` ignored in both request and route path, that is, if you try to access a route defined as `/index/page` you'll be able to access using `/index/page/` too.
>
> You can also force URLs to terminate with `/` by enabling [HttpServerConfiguration.ForceTrailingSlash](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpServerConfiguration.ForceTrailingSlash.md).
>
> Путь игнорирует завершающий `/` как в запросе, так и в шаблоне маршрута, то есть если вы попытаетесь обратиться к маршруту, определённому как `/index/page`, вы сможете обратиться к нему и как к `/index/page/`.
>
> Вы также можете принудительно добавлять завершающий `/` к URL, включив [HttpServerConfiguration.ForceTrailingSlash].

### Создание маршрутов с использованием экземпляров классов

You can also define routes dynamically using reflection with the attribute [RouteAttribute](https://docs.sisk-framework.org/api/Sisk.Core.Routing.RouteAttribute.md). This way, the instance of a class in which its methods implement this attribute will have their routes defined in the target router.

For a method to be defined as a route, it must be marked with a [RouteAttribute](https://docs.sisk-framework.org/api/Sisk.Core.Routing.RouteAttribute.md), such as the attribute itself or a [RouteGetAttribute](https://docs.sisk-framework.org/api/Sisk.Core.Routing.RouteGetAttribute.md). The method can be static, instance, public, or private. Use `MapInstance` when you want to map instance and static route methods from an object. Use `MapType` when you want to map only static route methods from a type.

```cs {title="Controller/MyController.cs"}
public class MyController
{
    // will match GET /
    // будет соответствовать GET /
    [RouteGet]
    HttpResponse Index(HttpRequest request)
    {
        HttpResponse res = new HttpResponse();
        res.Content = new StringContent("Index!");
        return res;
    }
    
    // static methods works too
    // статические методы тоже работают
    [RouteGet("/hello")]
    static HttpResponse Hello(HttpRequest request)
    {
        HttpResponse res = new HttpResponse();
        res.Content = new StringContent("Hello world!");
        return res;
    }
}
```

The line below will define both the `Index` and `Hello` methods of `MyController` as routes, as both are marked as routes, and an instance of the class has been provided, not its type. If its type had been provided instead of an instance, only the static methods would be defined.

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

To map only static route methods from a type, use:

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

Since Sisk version 0.16, it is possible to enable AutoScan, which will search for user-defined classes that implement `RouterModule` and will automatically associate it with the router. This is not supported with AOT compilation.

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

// The above instruction will search for all types which implements `ApiController` but **not the type itself**. The two optional parameters indicate how the method will search for these types. The first argument implies the Assembly where the types will be searched and the second indicates the way in which the types will be defined.
// Данная инструкция будет искать все типы, реализующие `ApiController`, но **не сам тип**. Два необязательных параметра указывают, как метод будет искать эти типы. Первый аргумент подразумевает сборку, в которой будет происходить поиск типов, а второй указывает способ определения этих типов.

## Маршруты с регулярными выражениями

Instead of using the default HTTP path matching methods, you can define a route which path is interpreted as a regular expression with the [RegexRoute](https://docs.sisk-framework.org/api/Sisk.Core.Routing.RegexRoute.md) class. Regex routes are [dynamic routes](#matching-precedence).

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

You can also capture groups from the regex pattern into the [HttpRequest.RouteParameters](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.RouteParameters.md) contents:

```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]
> The regular expression is not anchored automatically: a pattern such as `/uploads` also matches `/files/uploads/a`. Use `^` and `$` to match the whole path.
>
> Регулярное выражение не привязывается автоматически: шаблон вроде `/uploads` также совпадает с `/files/uploads/a`. Используйте `^` и `$`, чтобы сопоставить весь путь.

> [!IMPORTANT]
> Since Sisk 1.7, the `Route.UseRegex` property can no longer be set. It is obsolete and only reports whether the route is a [RegexRoute](https://docs.sisk-framework.org/api/Sisk.Core.Routing.RegexRoute.md). The `UseRegex` property of route attributes, such as `[Route(RouteMethod.Get, "...", UseRegex = true)]`, and the `[RegexRoute]` attribute keep working.
>
> Начиная с Sisk 1.7 свойство `Route.UseRegex` больше нельзя задавать. Оно устарело и лишь сообщает, является ли маршрут [RegexRoute](https://docs.sisk-framework.org/api/Sisk.Core.Routing.RegexRoute.md). Свойство `UseRegex` атрибутов маршрутов, например `[Route(RouteMethod.Get, "...", UseRegex = true)]`, и атрибут `[RegexRoute]` продолжают работать.

To migrate code that created regex routes with `Route.UseRegex`, replace the route with a `RegexRoute`:

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

// 1.7 and later
// 1.7 и позже
mainRouter.Map(new RegexRoute(RouteMethod.Get, @"^/items/(?<id>\d+)$", action));
```

## Префиксные маршруты

A [PrefixRoute](https://docs.sisk-framework.org/api/Sisk.Core.Routing.PrefixRoute.md) matches its path and every path nested under it, comparing whole path segments. It is the route used by [Router.MapFileSystem](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Router.MapFileSystem.md). Prefix routes are [dynamic routes](#matching-precedence).

```cs
// matches /docs, /docs/ and /docs/any/nested/path, but not /docsfoo
// соответствует /docs, /docs/ и /docs/any/nested/path, но не /docsfoo
mainRouter.Map(new PrefixRoute(RouteMethod.Get, "/docs", null, request =>
{
    return new HttpResponse($"Requested {request.Path}");
}, null));
```

## Пользовательское сопоставление маршрутов

You can create your own matching rules by inheriting [Route](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Route.md) and overriding [Route.Match](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Route.Match.md). The method receives the request path and the router that is matching the route, and returns a [RouteMatch](https://docs.sisk-framework.org/api/Sisk.Core.Routing.RouteMatch.md) with the result and, optionally, the captured route parameters.

The router validates the HTTP method only after `Match` succeeds, so `Match` should only test the path. This keeps the [405 Method Not Allowed](#method-not-allowed-405-callback-handler) and `OPTIONS` responses working for custom routes.

```cs {title="Routing/ExtensionRoute.cs"}
// matches any path ending with the given file extension, such as /reports/2024.csv
// соответствует любому пути, заканчивающемуся указанным расширением файла, например /reports/2024.csv
public sealed class ExtensionRoute : Route
{
    public ExtensionRoute(string extension, RouteAction action)
        : base(RouteMethod.Get, extension, action)
    {
    }

    // this route does not follow a path pattern
    // этот маршрут не следует шаблону пути
    public override bool IsStatic => false;

    // the path is not a path pattern, so it cannot be redirected to its trailing-slash form
    // путь не является шаблоном, поэтому его нельзя перенаправить в форму с завершающим слешем
    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");
}));
```

When overriding `Match`, keep in mind:

- Override [Route.IsStatic](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Route.IsStatic.md) to return `false` whenever the route does not strictly follow a path pattern. Otherwise, the router treats its `Path` as a path pattern when checking collisions and applying [Router.Prefix](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Router.Prefix.md), which can produce false collisions or invalid paths.
  // Переопределяйте [Route.IsStatic], возвращая `false`, когда маршрут не строго следует шаблону пути. Иначе роутер будет рассматривать `Path` как шаблон при проверке столкновений и применении [Router.Prefix], что может вызвать ложные столкновения или неверные пути.
- Override [Route.AllowRewrites](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Route.AllowRewrites.md) to return `false` when the route path cannot be redirected to its trailing-slash form by [HttpServerConfiguration.ForceTrailingSlash](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpServerConfiguration.ForceTrailingSlash.md).
  // Переопределяйте [Route.AllowRewrites], возвращая `false`, когда путь маршрута нельзя перенаправить в форму с завершающим слешем через [HttpServerConfiguration.ForceTrailingSlash].
- `Match` is called for every request that reaches the route, possibly from multiple threads at once. Avoid allocations when the route does not match and keep any cached state thread-safe. Return [RouteMatch.NotMatched](https://docs.sisk-framework.org/api/Sisk.Core.Routing.RouteMatch.NotMatched.md) for unsuccessful matches.
  // `Match` вызывается для каждого запроса, попадающего в маршрут, возможно из нескольких потоков одновременно. Избегайте выделений памяти, когда маршрут не совпадает, и держите кэшированное состояние потокобезопасным. Возвращайте [RouteMatch.NotMatched] для неуспешных совпадений.
- Respect [Router.MatchRoutesIgnoreCase](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Router.MatchRoutesIgnoreCase.md) through the `router` parameter.
  // Учитывайте [Router.MatchRoutesIgnoreCase] через параметр `router`.

## Добавление префикса к маршрутам

You can prefix all routes in a class or module with the [RoutePrefix](https://docs.sisk-framework.org/api/Sisk.Core.Routing.RoutePrefixAttribute.md) attribute and set the prefix as a string.

See the example below using the BREAD architecture (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()
    {
        ...
    }
}
```

In the above example, the HttpResponse parameter is omitted in favor of being used through the global context [HttpContext.Current](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpContext.Current.md). Read more in the section that follows.

## Маршруты без параметра запроса

Routes can be defined without the [HttpRequest](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.md) parameter and still be possible to obtain the request and its components in the request context. Let's consider an abstraction `ControllerBase` that serves as a foundation for all controllers of an API, and that abstraction provides the `Request` property to obtain the [HttpRequest](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpRequest.md) currently.

```cs {title="Controller/ControllerBase.cs"}
public abstract class ControllerBase
{
    // gets the request from the current thread
    // получает запрос из текущего потока
    public HttpRequest Request { get => HttpContext.Current.Request; }
    
    // the line below, when called, gets the database from the current HTTP session,
    // or creates a new one if it doesn't exist
    // строка ниже, при вызове, получает базу данных из текущей HTTP‑сессии,
    // или создаёт новую, если её нет
    public DbContext Database { get => HttpContext.Current.RequestBag.GetOrAdd<DbContext>(); }
}
```

And for all it's descendants to be able to use the route syntax without the request parameter:

```cs {title="Controller/UsersController.cs"}
[RoutePrefix("/api/users")]
public class UsersController : ControllerBase
{    
    [RoutePost]
    public async Task<HttpResponse> Create()
    {
        // reads the JSON data from the current request
        UserCreationDto? user = await Request.GetJsonContentAsync<UserCreationDto>();
        ...
        Database.Users.Add(user);
        
        return new HttpResponse(201);
    }
}
```

More details about the current context and dependency injection can be found in the [dependency injection](https://docs.sisk-framework.org/ru/docs/features/instancing.md) tutorial.

## Маршруты любого метода

You can define a route to be matched only by its path and skip the HTTP method. This can be useful for you to do method validation inside the route callback.

```cs
// will match / on any HTTP method
// будет соответствовать / для любого HTTP‑метода
mainRouter.MapAny("/", callbackFunction);
```

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

## Маршруты метода QUERY

Sisk supports the HTTP `QUERY` method, a safe and idempotent method that carries the query in the request body. 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) or the [RouteQuery](https://docs.sisk-framework.org/api/Sisk.Core.Routing.RouteQueryAttribute.md) attribute to define these routes:

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

The `Accept-Query` header, which advertises the media types accepted in `QUERY` requests, is available through [HttpHeaderCollection.AcceptQuery](https://docs.sisk-framework.org/api/Sisk.Core.Entity.HttpHeaderCollection.AcceptQuery.md) and [HttpKnownHeaderNames.AcceptQuery](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpKnownHeaderNames.AcceptQuery.md).

## Маршруты любого пути

Any path routes test for any path received by the HTTP server, subject to the route method being tested. If the route method is RouteMethod.Any and the route uses [Route.AnyPath](https://docs.sisk-framework.org/api/Sisk.Core.Routing.Route.AnyPath.md) in its path expression, this route will listen to all requests from the HTTP server, and no other routes can be defined.

```cs
// the following route will match all POST requests
// следующий маршрут будет соответствовать всем POST‑запросам
mainRouter.Map(RouteMethod.Post, Route.AnyPath, callbackFunction);
```

## Сопоставление маршрутов без учёта регистра

By default, the interpretation of routes with requests are case-sensitive. To make it ignore case, enable this option:

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

This will also enable the option `RegexOptions.IgnoreCase` for regex routes and case-insensitive comparison for prefix routes.

## Обработчик обратного вызова Not Found (404)

You can create a custom callback for when a request doesn't match any known routes.

```cs
mainRouter.NotFoundErrorHandler = () =>
{
    return new HttpResponse(404)
    {
        // Since v0.14
        // с версии 0.14
        Content = new HtmlContent("<h1>Not found</h1>")
        // older versions
        // более старые версии
        Content = new StringContent("<h1>Not found</h1>", Encoding.UTF8, "text/html")
    };
};
```

## Обработчик обратного вызова Method not allowed (405)

You can also create a custom callback for when a request matches it's path, but doens't match the method.

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

## Обработка ошибок

Exceptions can be thrown within a request lifecycle, which spans from the pre-execution request handler, through the router action, to the post-execution request handlers and value handlers. These exceptions are managed by the mechanism:

- If [HttpServerConfiguration.ThrowExceptions](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpServerConfiguration.ThrowExceptions.md) is `true`, exceptions will be thrown normally and will not be caught by Sisk, and the HTTP server may be interrupted if the exception is not caught.
  // Если [HttpServerConfiguration.ThrowExceptions] установлен в `true`, исключения будут выбрасываться обычным образом и не будут перехвачены Sisk, и HTTP‑сервер может быть прерван, если исключение не будет поймано.
- If [HttpServerConfiguration.ThrowExceptions](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpServerConfiguration.ThrowExceptions.md) is `false`, exceptions will be caught and handled by Sisk. After that, if `Router.CallbackErrorHandler` is defined, it will be called with the caught exception and the request context, and it **will not** be forwarded to the standard error output. If `Router.CallbackErrorHandler` is not defined, the exception will be forwarded to the standard error output, and the client will receive an HTTP 500 error response. If the standard error output is not defined, the error will be silently ignored.
  // Если [HttpServerConfiguration.ThrowExceptions] установлен в `false`, исключения будут перехвачены и обработаны Sisk. После этого, если определён `Router.CallbackErrorHandler`, он будет вызван с перехваченным исключением и контекстом запроса, и **не будет** передан в стандартный поток ошибок. Если `Router.CallbackErrorHandler` не определён, исключение будет передано в стандартный поток ошибок, и клиент получит ответ HTTP 500. Если стандартный поток ошибок не определён, ошибка будет тихо проигнорирована.

Note: within `Router.CallbackErrorHandler`, you can set the log mode for errors, access log, both, or none, and alter the default log writing behavior:

```csharp
router.CallbackErrorHandler = (ex, ctx) =>
{
    ctx.LogMode = LogOutput.Both; // override log mode to log the error in both access and error logs
}
```

## Внутренний обработчик ошибок

Route callbacks can throw errors during server execution. If not handled correctly, the overall functioning of the HTTP server can be terminated. The router has a callback for when a route callback fails and prevents service interruption.

This method is only reacheable when [ThrowExceptions](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpServerConfiguration.ThrowExceptions.md) is set to false.

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