Skip to content
Shiny.Maui.Shell v6 support for AI routing tools Learn More

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.

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 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}")]

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 maps a property to a specific part of the HTTP request. There are three parameter types:

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 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=name

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 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.

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 products
var products = await mediator.Request(new ListProductsRequest
{
Category = "electronics",
Page = 1
});
// Get single product
var product = await mediator.Request(new GetProductRequest { Id = "123" });
// Create product
var created = await mediator.Request(new CreateProductRequest
{
Body = new CreateProductDto { Name = "Widget", Price = 9.99m }
});
// Update product
var updated = await mediator.Request(new UpdateProductRequest
{
Id = "123",
Body = new UpdateProductDto { Name = "Updated Widget", Price = 12.99m }
});
// Delete product
var deleted = await mediator.Request(new DeleteProductRequest { Id = "123" });

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; }
}