let create_user_handler = ActionHandler(async fn(ctx) {
// Parse request body
let body = ctx.body
// ... validate and process
// Return success
ActionResult::ok(@js.any({ "id": 123, "name": "John" }))
})
let delete_user_handler = ActionHandler(async fn(ctx) {
// Check authorization
let user_id = ctx.get_query("id")
// ...
// Return redirect on success
ActionResult::redirect("/users")
})let registry = ActionRegistry::new(
allowed_origins=["https://example.com"]
)
.register(ActionDef::new("create-user", create_user_handler))
.register(
ActionDef::new("delete-user", delete_user_handler)
.with_require_json(false)
)let app = @hono.Hono::new()
let app = register_actions(app, registry)let response = invoke_action(
"/_action/create-user",
@js.any({ "name": "John", "email": "john@example.com" })
)
match response {
Success(data) => {
// Handle success
let id = data["id"]
}
Redirect(url) => {
// Handle redirect (usually automatic)
}
Error(status, message) => {
// Handle error
}
NetworkError(message) => {
// Handle network failure
}
}let create_user = create_action_invoker("/_action/create-user")
// Later...
let response = create_user(@js.any({ "name": "Jane" }))submit_form_as_action(
form_element,
ActionFormConfig::new("/_action/create-user")
)| Result | Description |
|---|---|
| Success(data) | Operation succeeded, return JSON data |
| Redirect(url) | Operation succeeded, redirect client |
| ClientError(status, msg) | Client error (4xx) |
| ServerError(msg) | Server error (logged, not exposed) |
// Full CSRF protection
@middleware.csrf_for_origin("https://example.com")
// Origin-only validation
@middleware.validate_origin_middleware(["https://example.com"])
// Fetch Metadata validation
@middleware.fetch_metadata()
// Require JSON Content-Type
@middleware.require_json_content_type()CsrfConfig::default()
.with_origins(["https://example.com", "https://api.example.com"])
.with_fetch_metadata(true)
.require_json()
.with_error_message("Access Denied")ActionDef::new("my-action", handler)
.with_require_json(true) // Require application/json
.with_middleware(auth_middleware) // Add custom middlewareActionRegistry::new(allowed_origins=["https://example.com"])
.with_base_path("/api/action") // Custom base path
.register(action1)
.register_all([action2, action3])pub struct ActionDef {
id : String
handler : ActionHandler
middlewares : Array[Middleware]
require_json : Bool
}pub struct ActionFormConfig {
action : String
form_method : String
on_success : (Any) -> Unit?
on_error : (Int, String) -> Unit?
on_redirect : (String) -> Bool?
}pub(all) struct ActionRef {
id : String
base_path : String
}// Define action ref (can be auto-generated)
pub fn create_user() -> ActionRef {
{ id: "create-user", base_path: "/_action" }
}
// Register with handler
registry.register_ref(@actions.create_user(), handler=handler)
// Client-side invocation
@action.invoke_action(@actions.create_user().url(), payload, callback)fn ActionRegistry::register_ref(self : ActionRegistry, aref : ActionRef, handler : ActionHandler) -> ActionRegistrypub enum ActionResult {
Success(Any)
Redirect(String)
HttpRedirect(String)
ClientError(Int, String)
ServerError(String)
}fn create_action_invoker(url : String, options? : ActionOptions) -> ((Any, (ActionResponse) -> Unit) -> Unit)fn create_action_invoker_ref(aref : ActionRef, options? : ActionOptions) -> ((Any, (ActionResponse) -> Unit) -> Unit)fn invoke_action(url : String, payload : Any, callback : (ActionResponse) -> Unit, options? : ActionOptions) -> Unitfn invoke_action_ref(aref : ActionRef, payload : Any, callback : (ActionResponse) -> Unit, options? : ActionOptions) -> UnitInstall
Download zipThin Mars wrapper for file-based routing, SSR, and asset loading