Handler endpoints bypass page routing, return raw fragments
Paths containing (api.) are intercepted before page routing and dispatched directly to the api function. The handler result is rendered to SX wire format and returned without layout wrapping. This fixes the issue where handler URLs went through page routing, causing the handler result to be passed as a slug to the page function, and the response to be wrapped in the full page layout. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2566,9 +2566,50 @@ let http_mode port =
|
||||
in
|
||||
write_response fd (http_response ~content_type:"text/plain; charset=utf-8" result); true
|
||||
end else
|
||||
(* Handler endpoints: paths containing "(api." are handler calls,
|
||||
not page renders. Evaluate the handler directly, return fragment. *)
|
||||
let is_handler_path =
|
||||
let rec has_sub s sub i =
|
||||
if i + String.length sub > String.length s then false
|
||||
else if String.sub s i (String.length sub) = sub then true
|
||||
else has_sub s sub (i + 1) in
|
||||
has_sub path "(api." 0 in
|
||||
let is_sx = path = "/sx/" || path = "/sx"
|
||||
|| (String.length path > 4 && String.sub path 0 4 = "/sx/") in
|
||||
if is_sx then begin
|
||||
if is_sx && is_handler_path then begin
|
||||
(* Handler dispatch — evaluate handler, return raw fragment *)
|
||||
let response =
|
||||
try
|
||||
let api_fn = env_get env "api" in
|
||||
(* Extract handler slug from path: ...api.SLUG)... *)
|
||||
let slug =
|
||||
let rec find_api s i =
|
||||
if i + 5 > String.length s then ""
|
||||
else if String.sub s i 5 = "(api." then
|
||||
let start = i + 5 in
|
||||
let end_ = try String.index_from s start ')' with Not_found -> String.length s in
|
||||
String.sub s start (end_ - start)
|
||||
else find_api s (i + 1) in
|
||||
find_api path 0 in
|
||||
let result = Sx_ref.cek_call api_fn (List [String slug]) in
|
||||
(* Render to SX wire format or HTML depending on content type *)
|
||||
let body_str = match result with
|
||||
| String s | SxExpr s -> s
|
||||
| Nil -> ""
|
||||
| _ ->
|
||||
let call = List [Symbol "aser"; List [Symbol "quote"; result]; Env env] in
|
||||
(match Sx_ref.eval_expr call (Env env) with
|
||||
| String s | SxExpr s -> s
|
||||
| v -> Sx_types.inspect v) in
|
||||
http_response ~content_type:"text/sx; charset=utf-8" body_str
|
||||
with e ->
|
||||
Printf.eprintf "[handler] Error for %s: %s\n%!" path (Printexc.to_string e);
|
||||
http_response ~status:500 ~content_type:"text/sx; charset=utf-8"
|
||||
(Printf.sprintf "(div :class \"p-4 text-rose-600\" \"Handler error: %s\")"
|
||||
(escape_sx_string (Printexc.to_string e)))
|
||||
in
|
||||
write_response fd response; true
|
||||
end else if is_sx then begin
|
||||
let cache_key = if is_ajax then "ajax:" ^ path else path in
|
||||
match Hashtbl.find_opt response_cache cache_key with
|
||||
| Some cached -> write_response fd cached; true
|
||||
|
||||
Reference in New Issue
Block a user