> ## Documentation Index
> Fetch the complete documentation index at: https://docs.neuro-tech.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Advanced patterns

> Take full control with raw requests, manual endpoints, parameter-name codecs, and service wiring

## When the high-level API is not enough

Most controller methods should stay in the normal attribute-based model. Reach for the advanced tools when you need:

* Manual control over how the request body is read
* Manual control over the exact response shape
* Low-level endpoint registration without controllers
* A custom JSON naming convention
* Constructor injection for controllers, middleware, and authorization handlers

## Raw requests

`[RawRequest]` disables automatic binding and response serialization for a controller method. When you use it:

* The method must return `Task`
* The method must take exactly two parameters
* The second parameter must be `HttpResponse`
* For `GET` and `DELETE`, the first parameter must be `ExtendedHttpRequest`
* For `POST`, `PUT`, and `PATCH`, the first parameter must be `HttpBodyRequest`

```csharp theme={null}
[Controller]
[Route("/orders")]
public sealed class OrdersController : ControllerBase
{
    [RawRequest]
    [HttpGet("/[orderId]")]
    public async Task Get(ExtendedHttpRequest Request, HttpResponse Response)
    {
        await Response.Return(new OrderStatusApiResponse
        {
            OrderId = Request.RouteParameters.Required("orderId"),
            Status = "queued"
        });
    }
}
```

In raw mode, you are responsible for reading all values and sending the response yourself.

## Manual endpoint registration

The router also supports a low-level style without controllers:

```csharp theme={null}
ControllerEndpointRouter Router = new ControllerEndpointRouter(
    "/api",
    true,
    new CamelCaseParameterNameCodec());

Router.RegisterGet("/users/[userId]", (ExtendedHttpRequest Request, HttpResponse Response) =>
{
    string UserId = Request.RouteParameters.Required("userId");
    return Response.Return(new UserApiResponse
    {
        UserId = UserId,
        DisplayName = "Ada"
    });
});

Router.RegisterOptions("/users/[userId]", (ExtendedHttpRequest Request, HttpResponse Response) =>
{
    Response.SetHeader("Allow", "GET,OPTIONS");
    return Response.SendResponse();
});
```

This style is useful when you want a lightweight route table or need to build endpoints dynamically.

## Parameter-name codecs

The router uses `IParameterNameCodec` to map CLR member names to JSON field names. Built-in codecs:

* `CamelCaseParameterNameCodec`
* `SnakeCaseParameterNameCodec`
* `TransparentParameterNameCodec`

You can configure the codec directly on the router:

```csharp theme={null}
ControllerEndpointRouter Router = new ControllerEndpointRouter(
    "/api",
    true,
    new SnakeCaseParameterNameCodec());
Router.RegisterControllers(typeof(UsersController).Assembly);
```

Use the codec that matches the JSON contract you want before controller registration:

```csharp theme={null}
ControllerEndpointRouter Router = new ControllerEndpointRouter(
    "/api",
    true,
    new CamelCaseParameterNameCodec());
Router.RegisterControllers(typeof(UsersController).Assembly);
```

The same codec is used for both request body decoding and response encoding.

## Service provider and constructor injection

The router exposes a built-in `HttpServiceProvider` through `Router.ServiceProvider`.

Constructor injection means the router creates your controller, middleware, or authorization class and supplies the constructor arguments for you.

Instead of this:

```csharp theme={null}
public sealed class NotificationsController : ControllerBase
{
    private readonly IClock clock;

    public NotificationsController()
    {
        this.clock = new SystemClock();
    }
}
```

you let the router provide the dependency:

```csharp theme={null}
public sealed class NotificationsController : ControllerBase
{
    private readonly IClock clock;

    public NotificationsController(IClock Clock)
    {
        this.clock = Clock;
    }
}
```

That keeps object creation in one place, makes the controller easier to test, and lets the same dependency graph be reused for controllers, middleware, and authorization handlers.

## How to use it

The typical flow is:

1. Register shared dependencies on `Router.ServiceProvider`.
2. Add constructor parameters to the controller, middleware, or authorization class.
3. Register the controller or scan the assembly.
4. Let the router resolve the constructor when it creates the instance.

```csharp theme={null}
ControllerEndpointRouter Router = new ControllerEndpointRouter(
    "/api",
    true,
    new CamelCaseParameterNameCodec());

Router.ServiceProvider.RegisterSingleton(typeof(IClock), new SystemClock());
Router.ServiceProvider.RegisterService(typeof(INotificationSink), new AuditSink());
Router.ServiceProvider.RegisterService(typeof(INotificationSink), new MetricsSink());

Router.RegisterController<NotificationsController>();
```

```csharp theme={null}
[Controller]
[Route("/notifications")]
public sealed class NotificationsController : ControllerBase
{
    private readonly IClock clock;
    private readonly INotificationSink[] sinks;

    public NotificationsController(IClock Clock, INotificationSink[] Sinks)
    {
        this.clock = Clock;
        this.sinks = Sinks;
    }

    [HttpGet("/probe")]
    public Task<NotificationProbeResponse> Probe()
    {
        return Task.FromResult(new NotificationProbeResponse
        {
            Timestamp = this.clock.UtcNow,
            SinkCount = this.sinks.Length
        });
    }
}

public interface IClock
{
    DateTimeOffset UtcNow { get; }
}

public sealed class SystemClock : IClock
{
    public DateTimeOffset UtcNow => DateTimeOffset.UtcNow;
}

public interface INotificationSink
{
}

public sealed class AuditSink : INotificationSink
{
}

public sealed class MetricsSink : INotificationSink
{
}

public sealed class NotificationProbeResponse
{
    public DateTimeOffset Timestamp { get; set; }
    public int SinkCount { get; set; }
}
```

## What the router can resolve

Current constructor resolution supports:

* Singletons registered with `RegisterSingleton(...)`
* Multi-registration services registered with `RegisterService(...)`
* `IEnumerable<T>` and `T[]` constructor parameters for multi-registrations
* Unregistered concrete dependencies when the provider can build their public constructor graph

In practice that means:

* Use `RegisterSingleton(...)` when one shared instance should be reused everywhere
* Use `RegisterService(...)` when you want to inject a collection of registered implementations
* Use a concrete class parameter when the provider can recursively construct that class from other resolvable dependencies

The same provider is used for controller instances, middleware declared with `[Middleware]`, and authorization middleware declared with `[UseAuthorization]`.

## Lifetime model

It helps to separate the lifetime of the object being created from the lifetime of the dependencies being injected:

* Controllers are created per request
* Middleware attribute classes are created through the service provider when the router wires them up
* Authorization middleware declared with `[UseAuthorization]` is also created through the service provider
* Singleton dependencies are reused across every resolved instance

That means request-specific state should usually live on the controller instance or in `ControllerBase.Context`, not inside shared singleton dependencies.

## Constructor selection guidance

If a class has multiple public constructors, the provider picks the best resolvable one. That is useful, but in application code it is usually better to keep one clear public constructor for each controller or middleware class.

That gives you:

* More predictable wiring
* Fewer accidental constructor-selection surprises
* Cleaner registration failures when a dependency is missing

## Common usage patterns

Constructor injection is most useful when:

* A controller needs shared collaborators such as clocks, repositories, or API clients
* Middleware needs reusable services such as audit writers or feature flags
* Authorization handlers need access to permission stores or identity services

Keep constructor injection for long-lived collaborators. Use method parameters for route, query, and body data, and use `Context` for request-specific transport state.

## Practical rules

* Register dependencies before calling `RegisterController<T>()` or `RegisterControllers(...)`
* Prefer interfaces for shared services and concrete types for simple helper objects
* Use `IEnumerable<T>` or `T[]` when multiple implementations should be injected together
* Keep a single obvious public constructor when possible
* If a dependency cannot be resolved, simplify the constructor graph first before adding more registrations

## Route helper

`ControllerBase.GetRoute<T>()` returns the `[Route]` prefix declared on a controller and caches the result.

```csharp theme={null}
string NotificationsRoute = ControllerBase.GetRoute<NotificationsController>();
```

## A simple rule of thumb

Stay with normal attributed methods until you have a concrete reason not to. Use raw requests when:

* The body shape is unusual
* You need direct access to parser output
* You want full control over the response lifecycle

Use manual endpoint registration when:

* Controllers would add ceremony without helping
* Routes are generated or highly dynamic
* You want to build a focused low-level module
