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.

Binding and Responses

Learn how the router binds route, query, and body values and turns return values into JSON

The default binding model

For attributed controller methods, the router infers where values come from:
  • For POST, PUT, and PATCH, one class-type parameter is treated as the request body
  • Parameters whose names match route placeholders are read from the route
  • Remaining scalar parameters are read from the query string

Implicit binding example

[Controller]
[Route("/orders")]
public sealed class OrdersController : ControllerBase
{
    [HttpPost("/[orderId]")]
    public Task<OrderResponse> Update(OrderUpdate Body, string OrderId, string? Sort)
    {
        return Task.FromResult(new OrderResponse
        {
            OrderId = OrderId,
            Sort = Sort,
            Description = Body.Description
        });
    }
}

public sealed class OrderUpdate
{
    public string Description { get; set; } = string.Empty;
}

public sealed class OrderResponse
{
    public string OrderId { get; set; } = string.Empty;
    public string? Sort { get; set; }
    public string Description { get; set; } = string.Empty;
}
  • Body — from the JSON body
  • OrderId — from /orders/[orderId]
  • Sort — from ?sort=...

Explicit binding example

[HttpPost("/[orderId]")]
public Task<OrderResponse> Update(
    [FromBody] OrderUpdate Body,
    [FromRoute] string OrderId,
    [FromQuery] string? Sort)
{
    return Task.FromResult(new OrderResponse
    {
        OrderId = OrderId,
        Sort = Sort,
        Description = Body.Description
    });
}
Explicit attributes:
  • [FromBody]
  • [FromRoute]
  • [FromQuery]

Important binding rules

  • Only one body parameter is allowed
  • [FromBody] is only valid on POST, PUT, and PATCH
  • [FromBody] must target a class-type parameter
  • Missing required route or query values return 400 Bad Request
  • Nullable query parameters can be omitted and bind as null

Returning JSON

Non-raw controller methods should return Task<T>. The returned object is serialized to JSON automatically.
[HttpGet("/[userId]")]
public Task<UserResponse> Get(string UserId)
{
    return Task.FromResult(new UserResponse
    {
        UserId = UserId,
        DisplayName = "Ada"
    });
}

Returning errors

For attributed methods, throw an HTTP exception:
[HttpGet("/[userId]")]
public Task<UserResponse> Get(string UserId)
{
    if (UserId == "missing")
        throw new NotFoundException(new RestApiErrorResponse("User not found.", "user_not_found"));

    if (UserId == "bad")
        throw new BadRequestException(new RestApiErrorResponse("User id is invalid.", "invalid_user_id"));

    return Task.FromResult(new UserResponse
    {
        UserId = UserId,
        DisplayName = "Ada"
    });
}
Useful helpers:
  • RestApiErrorResponse — simple { message, error_code } payload
  • ClientError — carries status code, message, localization key, and error code
  • Expected<T, E> — models success and failure explicitly before converting to HTTP
If an unexpected exception escapes the controller, the router converts it into an internal server error with code internal_server_exception. Middleware and Pipeline shows how to add cross-cutting behavior before and after your endpoints run.