Request Contracts
HTTP request contracts define the shape of your API calls using interfaces and attributes. Every HTTP contract implements IHttpRequest<TResult> and is decorated with attributes that describe the HTTP verb, route, parameters, and body.
IHttpRequest<TResult>
Section titled “IHttpRequest<TResult>”All HTTP contracts must implement IHttpRequest<TResult> where TResult is the deserialized response type.
using Shiny.Mediator.Http;
[Http(HttpVerb.Get, "/api/users/{Id}")]public class GetUserRequest : IHttpRequest<UserResponse>{ [HttpParameter(HttpParameterType.Path)] public string Id { get; set; }}The [Http] Attribute
Section titled “The [Http] Attribute”The [Http] attribute defines the HTTP verb and route template for the request.
[Http(HttpVerb.Get, "/api/resource")][Http(HttpVerb.Post, "/api/resource")][Http(HttpVerb.Put, "/api/resource/{Id}")][Http(HttpVerb.Patch, "/api/resource/{Id}")][Http(HttpVerb.Delete, "/api/resource/{Id}")]HTTP Verb Examples
Section titled “HTTP Verb Examples”GET - Retrieve data
[Http(HttpVerb.Get, "/api/products")]public class GetProductsRequest : IHttpRequest<List<Product>>{ [HttpParameter(HttpParameterType.Query)] public string Category { get; set; }
[HttpParameter(HttpParameterType.Query)] public int Page { get; set; } = 1;}POST - Create a resource
[Http(HttpVerb.Post, "/api/products")]public class CreateProductRequest : IHttpRequest<Product>{ [HttpBody] public CreateProductDto Body { get; set; }}PUT - Replace a resource
[Http(HttpVerb.Put, "/api/products/{Id}")]public class UpdateProductRequest : IHttpRequest<Product>{ [HttpParameter(HttpParameterType.Path)] public string Id { get; set; }
[HttpBody] public UpdateProductDto Body { get; set; }}PATCH - Partially update a resource
[Http(HttpVerb.Patch, "/api/products/{Id}")]public class PatchProductRequest : IHttpRequest<Product>{ [HttpParameter(HttpParameterType.Path)] public string Id { get; set; }
[HttpBody] public PatchProductDto Body { get; set; }}DELETE - Remove a resource
[Http(HttpVerb.Delete, "/api/products/{Id}")]public class DeleteProductRequest : IHttpRequest<Product>{ [HttpParameter(HttpParameterType.Path)] public string Id { get; set; }}The [HttpParameter] Attribute
Section titled “The [HttpParameter] Attribute”The [HttpParameter] attribute maps a property to a specific part of the HTTP request. There are three parameter types:
Path Parameters
Section titled “Path Parameters”Path parameters are substituted into the route template. The property name must match the {placeholder} in the route.
[Http(HttpVerb.Get, "/api/users/{UserId}/orders/{OrderId}")]public class GetOrderRequest : IHttpRequest<OrderResponse>{ [HttpParameter(HttpParameterType.Path)] public string UserId { get; set; }
[HttpParameter(HttpParameterType.Path)] public string OrderId { get; set; }}Query Parameters
Section titled “Query Parameters”Query parameters are appended to the URL as query string values.
[Http(HttpVerb.Get, "/api/search")]public class SearchRequest : IHttpRequest<SearchResults>{ [HttpParameter(HttpParameterType.Query)] public string Term { get; set; }
[HttpParameter(HttpParameterType.Query)] public int PageSize { get; set; } = 25;
[HttpParameter(HttpParameterType.Query)] public string SortBy { get; set; }}
// Resulting URL: /api/search?Term=hello&PageSize=25&SortBy=nameValue Formatting
Section titled “Value Formatting”Path, query and header values are formatted by HttpParameterFormatter, so the URL is the same no matter which culture the device runs in:
| Type | Sent as |
|---|---|
DateTimeOffset, DateTime |
Round-trip ISO 8601 (2026-09-04T21:46:57.0000000+00:00) |
DateOnly |
yyyy-MM-dd |
TimeOnly |
HH:mm:ss (fractional seconds when present) |
Numbers (int, decimal, double, …) |
Invariant culture (1234.5, never 1234,5) |
bool |
true / false |
| Enums | Member name (numeric OpenAPI enums are sent as their value) |
Path and query values are URL-encoded, and null query values are skipped. A collection property (List<T>, T[], HashSet<T>, …) repeats the parameter once per item:
[HttpParameter(HttpParameterType.Query)]public List<string> Tag { get; set; } // ?Tag=a&Tag=bDon’t escape or pre-format values yourself — they would be encoded twice.
Header Parameters
Section titled “Header Parameters”Header parameters are added as HTTP request headers.
[Http(HttpVerb.Get, "/api/data")]public class GetDataRequest : IHttpRequest<DataResponse>{ [HttpParameter(HttpParameterType.Header)] public string ApiVersion { get; set; } = "2.0";
[HttpParameter(HttpParameterType.Header)] public string CorrelationId { get; set; }}The [HttpBody] Attribute
Section titled “The [HttpBody] Attribute”The [HttpBody] attribute marks a property to be serialized as the JSON request body.
[Http(HttpVerb.Post, "/api/orders")]public class CreateOrderRequest : IHttpRequest<OrderResponse>{ [HttpBody] public OrderDto Order { get; set; }}There can only be one [HttpBody] property per contract. The body is serialized to JSON.
Complete CRUD Example
Section titled “Complete CRUD Example”Here’s a full set of contracts for a typical CRUD API:
using Shiny.Mediator;using Shiny.Mediator.Http;
// List[Http(HttpVerb.Get, "/api/products")]public class ListProductsRequest : IHttpRequest<List<Product>>{ [HttpParameter(HttpParameterType.Query)] public string Category { get; set; }
[HttpParameter(HttpParameterType.Query)] public int Page { get; set; } = 1;
[HttpParameter(HttpParameterType.Query)] public int PageSize { get; set; } = 25;}
// Get by ID[Http(HttpVerb.Get, "/api/products/{Id}")]public class GetProductRequest : IHttpRequest<Product>{ [HttpParameter(HttpParameterType.Path)] public string Id { get; set; }}
// Create[Http(HttpVerb.Post, "/api/products")]public class CreateProductRequest : IHttpRequest<Product>{ [HttpBody] public CreateProductDto Body { get; set; }}
// Update[Http(HttpVerb.Put, "/api/products/{Id}")]public class UpdateProductRequest : IHttpRequest<Product>{ [HttpParameter(HttpParameterType.Path)] public string Id { get; set; }
[HttpBody] public UpdateProductDto Body { get; set; }}
// Delete[Http(HttpVerb.Delete, "/api/products/{Id}")]public class DeleteProductRequest : IHttpRequest<Product>{ [HttpParameter(HttpParameterType.Path)] public string Id { get; set; }}Usage:
IMediator mediator; // injected
// List productsvar products = await mediator.Request(new ListProductsRequest{ Category = "electronics", Page = 1});
// Get single productvar product = await mediator.Request(new GetProductRequest { Id = "123" });
// Create productvar created = await mediator.Request(new CreateProductRequest{ Body = new CreateProductDto { Name = "Widget", Price = 9.99m }});
// Update productvar updated = await mediator.Request(new UpdateProductRequest{ Id = "123", Body = new UpdateProductDto { Name = "Updated Widget", Price = 12.99m }});
// Delete productvar deleted = await mediator.Request(new DeleteProductRequest { Id = "123" });Combining All Parameter Types
Section titled “Combining All Parameter Types”A single contract can use path, query, header, and body parameters together:
[Http(HttpVerb.Post, "/api/v1/tenants/{TenantId}/items")]public class CreateItemRequest : IHttpRequest<ItemResponse>{ [HttpParameter(HttpParameterType.Path)] public string TenantId { get; set; }
[HttpParameter(HttpParameterType.Query)] public bool Validate { get; set; } = true;
[HttpParameter(HttpParameterType.Header)] public string CorrelationId { get; set; }
[HttpBody] public CreateItemDto Body { get; set; }}

