Actions
Table of contents
Actions represent operations a client can perform on an HTO. They appear in the Siren actions array — but only when the action property is non-null and CanExecute() returns true.
Defining an action
Create a nested class deriving from HypermediaAction<TParameter> (or HypermediaAction for parameter-less actions). Add it as a property on your HTO:
[HypermediaObject(Title = "A Customer", Classes = new[] { "Customer" })]
public class HypermediaCustomerHto : IHypermediaObject
{
[HypermediaAction(Name = "CustomerMove", Title = "A Customer moved to a new location.")]
public CustomerMoveOp CustomerMove { get; set; }
[HypermediaAction(Name = "MarkAsFavorite", Title = "Marks a Customer as a favorite buyer.")]
public MarkAsFavoriteOp MarkAsFavorite { get; set; }
// Action with parameter
public class CustomerMoveOp : HypermediaAction<NewAddress>
{
public CustomerMoveOp(Func<bool> canExecute) : base(canExecute) { }
}
// Action with parameter
public class MarkAsFavoriteOp : HypermediaAction<MarkAsFavoriteParameters>
{
public MarkAsFavoriteOp(Func<bool> canExecute) : base(canExecute) { }
}
}
The [HypermediaAction] attribute is optional — use it to give the action a fixed name or title in the Siren output. Without it, the property name is used.
Action parameters
Action parameters implement IHypermediaActionParameter. Records are recommended:
public record NewAddress(AddressTo Address) : IHypermediaActionParameter;
public record AddressTo(string Street, string Number, string City, string ZipCode);
Required properties of the parameter type appear as fields in the Siren action. By default, RESTyard auto-generates a JSON schema endpoint for each parameter type so clients can discover the expected structure (see Configuration).
CanExecute gate
The Func<bool> passed to the constructor controls whether the action appears in the Siren output. This lets the server tell the client what operations are currently available:
// In the controller or service that builds the HTO:
var customerMove = new CustomerMoveOp(canExecute: () => customer.IsActive);
If CanExecute() returns false, the action is omitted entirely from the response.
Null action properties
As an alternative to the CanExecute gate, you can set an action property to null to omit it from the Siren output. This is useful when an action availability check is not done in CanExecute():
// Action is available — no CanExecute needed (defaults to always true)
CustomerMove = new CustomerMoveOp();
// Action is not available — null means omitted from Siren output
CustomerMove = null;
All action base classes support a parameterless constructor that defaults CanExecute to true. This lets you use nullable action properties as the primary visibility mechanism. CanExecute is still availble to hold for conditional logic.
Prefilled values
Actions can supply default values so a client form is pre-populated. Pass them as the second constructor argument:
public class CreateQueryOp : HypermediaAction<CustomerQuery>
{
public CreateQueryOp(Func<bool> canExecute, CustomerQuery? prefilledValues = default)
: base(canExecute, prefilledValues)
{
}
}
Prefilled values are serialized into the Siren fields as value.
Parameter-less actions
For actions without parameters, derive from HypermediaAction (no type parameter):
public class MarkAsDoneOp : HypermediaAction
{
public MarkAsDoneOp(Func<bool> canExecute) : base(canExecute) { }
}
File upload actions
Use FileUploadHypermediaAction or FileUploadHypermediaAction<TParameter> for actions that accept file uploads. Pass FileUploadConfiguration to inform the client about allowed behavior (file size, type, single vs. multiple):
public class UploadCarImageOp : FileUploadHypermediaAction<UploadCarImageParameters>
{
public UploadCarImageOp(Func<bool> canExecute, FileUploadConfiguration? fileUploadConfiguration = null)
: base(canExecute, fileUploadConfiguration)
{
}
}
See Endpoints — File upload actions for the controller-side setup.
External actions
Use HypermediaExternalAction<TParameter> or HypermediaExternalAction for actions that call an external API instead of a local endpoint:
public class ExternalActionNoParameters : HypermediaExternalAction
{
public ExternalActionNoParameters(Uri externalUri, string httpMethod)
: base(() => true, externalUri, httpMethod) { }
}
public class ExternalActionWithArgument : HypermediaExternalAction<ExternalActionParameters>
{
public ExternalActionWithArgument(
Uri externalUri,
string httpMethod,
string acceptedMediaType,
ExternalActionParameters prefilledValues)
: base(() => true, externalUri, httpMethod, acceptedMediaType, prefilledValues) { }
}
public class ExternalActionParameters : IHypermediaActionParameter
{
public int AInt { get; }
public ExternalActionParameters(int aInt) { AInt = aInt; }
}
// Usage in HTO:
public ExternalActionNoParameters ExternalAction { get; init; }
= new ExternalActionNoParameters(new Uri("http://www.example.com"), HttpMethod.POST);
public ExternalActionWithArgument ExternalActionWithArgs { get; init; }
= new ExternalActionWithArgument(
new Uri("http://www.example2.com"),
HttpMethod.DELETE,
"application/json",
new ExternalActionParameters(3));
External actions render with the external URI as href in the Siren output instead of a locally resolved route.
Dynamic actions
If an action’s parameters (or whether it has any) are determined at runtime, use DynamicHypermediaAction. See Dynamic actions for details.
Action types summary
| Base class | Use case |
|---|---|
HypermediaAction | Parameter-less action |
HypermediaAction<TParameter> | Action with typed parameters |
FileUploadHypermediaAction | File upload without extra parameters |
FileUploadHypermediaAction<TParameter> | File upload with extra parameters |
HypermediaExternalAction | External API call, no parameters |
HypermediaExternalAction<TParameter> | External API call with parameters |
DynamicHypermediaAction | Runtime-determined parameters |