From 70c5199df493bf3ec83925e83ccd385d5a092f7d Mon Sep 17 00:00:00 2001 From: Yorhel Date: Sat, 8 Mar 2025 15:49:43 +0100 Subject: [PATCH] FU: Add JSON reading & writing methods --- FU.pm | 43 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/FU.pm b/FU.pm index 4807e0b..1b3f6aa 100644 --- a/FU.pm +++ b/FU.pm @@ -637,7 +637,10 @@ sub headers { $FU::REQ->{hdr} } sub ip { $FU::REQ->{ip} } sub _getfield($data, @a) { - return $data->{$a[0]} if @a == 1 && !ref $a[0]; + if (@a == 1 && !ref $a[0]) { + fu->error(400, "Expected top-level to be a hash") if ref $data ne 'HASH'; + return $data->{$a[0]}; + } require FU::Validate; my $schema = FU::Validate->compile(@a > 1 ? { keys => {@a} } : $a[0]); my $res = eval { $schema->validate($data) }; @@ -652,6 +655,15 @@ sub query { _getfield $FU::REQ->{qs_parsed}, @_; } +sub json { + shift; + $FU::REQ->{json} ||= eval { + FU::Util::json_parse($FU::REQ->{data}, utf8 => 1) + } || fu->error(400, "JSON parse error: $@"); + return $FU::REQ->{json} if !@_; + _getfield $FU::REQ->{json}, @_; +} + sub formdata { shift; $FU::REQ->{formdata} ||= eval { @@ -715,6 +727,12 @@ sub set_header($, $hdr, $val=undef) { $FU::REQ->{reshdr}{ lc $hdr } = $val; } +sub send_json($, $data) { + fu->set_header('content-type', 'application/json'); + fu->set_body(FU::Util::json_format($data, canonical => 1, utf8 => 1)); + fu->done; +} + sub send_file($, $root, $path) { # This also catches files with '..' somewhere in the middle of the name. # Let's just disallow that to simplify this check, I'd err on the side of @@ -1242,9 +1260,12 @@ Parse, validate and return multiple query parameters. # Or, more concisely: my $data = fu->query(a => {anybool => 1}, b => {}); -=item fu->formdata($name) +=item fu->json(@args) -=item fu->formdata($schema) +Like C<< fu->query() >> but parses the request body as JSON. Returns the +decoded JSON value if C<@args> is empty. + +=item fu->formdata(@args) Like C<< fu->query() >> but returns data from the POST request body. This method only supports form data encoded as C, @@ -1253,13 +1274,11 @@ use C<< fu->multipart >> instead. =item fu->multipart -Parse the request body as C and return an array of fields. -Refer to L for more information. +Parse the request body as C and return an array of field +objects. Refer to L for more information. =back -I Support JSON bodies. - I Cookie parsing. @@ -1321,6 +1340,14 @@ templating system or L: }; }); +=item fu->send_json($data) + +Encode C<$data> as JSON (using C in L), set an +appropriate C header and send it to the client. Calls C<< +fu->done >>. + +I Support schema-based normalization. + =item fu->send_file($root, $path) If a file identified by C<"$root/$path"> exists, set that as response and call @@ -1374,8 +1401,6 @@ one of the following status codes or an alias: I Setting cookies. -I JSON output. - =head2 Running the Site