Skip to main content

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.

Controllers and Routes

Build endpoints with controller classes, route attributes, and route templates

Start with a controller

Use [Controller] to make a class discoverable and [Route] to define its base path.
[Controller]
[Route("/users")]
public sealed class UsersController : ControllerBase
{
    [HttpGet("")]
    public Task<List<UserSummary>> List()
    {
        return Task.FromResult(new List<UserSummary>
        {
            new UserSummary { UserId = "1", DisplayName = "Ada" },
            new UserSummary { UserId = "2", DisplayName = "Linus" }
        });
    }

    [HttpGet("/me")]
    public Task<UserSummary> GetCurrent()
    {
        return Task.FromResult(new UserSummary
        {
            UserId = "me",
            DisplayName = "Current user"
        });
    }

    [HttpGet("/[userId]")]
    public Task<UserSummary> Get(string UserId)
    {
        return Task.FromResult(new UserSummary
        {
            UserId = UserId,
            DisplayName = "Ada"
        });
    }
}

public sealed class UserSummary
{
    public string UserId { get; set; } = string.Empty;
    public string DisplayName { get; set; } = string.Empty;
}
The full routes become:
  • GET /api/users
  • GET /api/users/me
  • GET /api/users/[userId]

HTTP method attributes

Use one of these on controller methods:
  • [HttpGet("...")]
  • [HttpPost("...")]
  • [HttpPut("...")]
  • [HttpPatch("...")]
  • [HttpDelete("...")]
The controller route and method route are concatenated.

Route template segments

  • users — literal segment
  • [userId] — named route parameter
  • * — any single segment
[Controller]
[Route("/projects")]
public sealed class ProjectsController : ControllerBase
{
    [HttpGet("/[projectId]/files/*")]
    public Task<ProjectFileResponse> GetFile(string ProjectId)
    {
        return Task.FromResult(new ProjectFileResponse
        {
            ProjectId = ProjectId
        });
    }
}

public sealed class ProjectFileResponse
{
    public string ProjectId { get; set; } = string.Empty;
}

How parameter matching works

Route placeholder names are matched against method parameter names case-insensitively. [HttpGet("/[userId]")] can bind to string UserId. Path matching is case-sensitive; route placeholder name matching is case-insensitive.

Controller lifecycle

The router creates a new controller instance per request:
  • Request-specific state does not leak between requests
  • ControllerBase.Context always belongs to the current request
  • Do not access ControllerBase.Context in the constructor

When to omit [Route]

If you omit [Route], the controller base path defaults to /. Most real applications are easier to read when each controller owns a clear path prefix. Binding and Responses shows how route values, query strings, and JSON bodies are turned into controller method arguments.