.response~content_type = 'text/html; charset=utf-8'
Say '<!DOCTYPE html>'
Say '<html lang="en">'
Say ' <head>'
Say " <title>Fetching and merging JSON from the web, returning HTML</title>"
Say ' </head>'
Say ' <body>'
Say ' <h1>Two posts, fetched and merged</h1>'
Say ' <p>This rexxlet fetches two sample posts from the public'
Say ' <a href="https://jsonplaceholder.typicode.com/">JSONPlaceholder</a>'
Say ' API, parses their JSON, and shows them below. The placeholder text'
Say ' is theirs — what this example demonstrates is a rexxlet reaching'
Say ' out to the network and composing the results.</p>'
Base = "https://jsonplaceholder.typicode.com/posts/"
Do id = 1 To 2
post = FetchJSON(Base || id)
If post == .nil Then
Say ' <p>Could not load post 'id'.</p>'
Else Do
Say ' <article>'
Say ' <h2>#'.response~encodeHTML(post["id"])' — '.response~encodeHTML(post["title"])'</h2>'
Say ' <p>'.response~encodeHTML(post["body"])'</p>'
Say ' </article>'
End
End
Say ' <p>'
Say ' <small><a href="../">⬅️ Back to the examples.</a></small>'
Say ' </p>'
Say ' </body>'
Say '</html>'
Exit
-- Fetch a URL and parse the JSON body into a Rexx object.
-- Returns .nil if the download fails or the body is not valid JSON.
-- Note: this blocks the CGI process until curl returns, so we keep it
-- on a short leash -- --connect-timeout caps the connect phase at 5s,
-- and --max-time caps the whole transfer at 15s, so a dead remote host
-- cannot hang the request. -f makes curl return a non-zero RC on HTTP
-- >= 400 instead of handing back the error page as if it were the body.
-- curl ships with Windows 10/11, macOS and most Linux; wget does not.
FetchJSON: Procedure
Use Strict Arg url
-- Keep the probe silent in Apache's error.log when curl is absent. Two
-- INDEPENDENT channels would otherwise write there, so both are muted:
-- * the interpreter's trace: a missing binary is a FAILURE, which the
-- default Trace Normal echoes. Trace("Off") silences it; Trace(setting)
-- pushes the new setting and returns the previous one, restored below.
-- * the child's stderr (the shell's "curl: not found"): Error Stem
-- captures it instead of letting it flow through to the server log.
-- Neither subsumes the other. The .nil return below already handles curl
-- being absent or the fetch failing.
savedTrace = Trace("Off")
Address Command "curl -sf --connect-timeout 5 --max-time 15" url With Output Stem lines. Error Stem discard.
Call Trace savedTrace
If RC \= 0 Then Return .nil
text = ""
Do i = 1 To lines.0
If i > 1 Then text = text || "0a"x
text = text || lines.i
End
Signal On Syntax Name BadJSON
Return .json~fromJson(text)
BadJSON:
Return .nil
::Requires "json.cls"