Webservice Response
Builds the HTTP response that a webservice rule sends back to its caller. You write one YAML map, and each key in it either contributes to the response payload or sets one part of the HTTP envelope: the status, the headers, the cookies, the redirect target.
Without this op a webservice rule still answers, but with whatever the rule happened to return and a
__msg entry in the payload saying no webservice response was set on the rule. Reach for this op as
soon as the caller cares about the shape of the answer.
The op has no effect in any other rule type. It writes its values away and nothing reads them back, with no warning in the designer and no error at run time.
Fields¶
Response¶
The only control on the form. A YAML editor holding one map, where every top-level key is one of the
names in the table below. An unknown key is accepted and then ignored when the response is built, so
a typo such as statsu: 404 costs you the status and says nothing about it.
${var} placeholders expand against the stash in values, at any nesting depth,
inside nested maps and lists alike. Keys are left untouched, so ${foo}: 1 reaches the caller with
the literal text as the key name. ${ws_params.foo} reads a query parameter and
${ws_headers.host} reads a request header.
The editor parses the YAML when you save the op, not when the rule runs. That has two consequences. Invalid YAML raises a dialog and blocks the save, so a broken response never reaches a running rule. And comments are dropped, key order is not kept, and the map comes back sorted alphabetically the next time you open the form. Put any explanation you need in the op's Note tab instead.
Leave the field empty and the op does nothing at all. The same happens when the top level is a list rather than a map. Neither case logs anything, and the caller gets the default no-response payload.
Response keys¶
| Key | Effect |
|---|---|
data: |
contributes to a structured payload, serialised according to the calling URL |
body: |
sends a string back verbatim, for text or file contents |
status: |
sets the HTTP status code |
headers: |
a map of header names to values |
header: |
accepted, applied, and changes nothing |
content_type: |
shorthand for the Content-Type header |
cookies: |
a map of cookie names to cookie settings |
location: |
sets the Location header and leaves the status alone |
redirect: |
sets Location and forces the status to 302 |
write: |
pushes a chunk straight to the client |
data: and body:¶
The two are mutually exclusive, and the check spans the whole request rather than a single op. If
one op sets body: and a second op anywhere later in the rule sets data:, the request fails with
a message quoting the first 20 characters of each side. Decide which of the two the rule uses and
stay with it on every branch.
data: accepts a map or a list. A map merges key by key into whatever earlier ops contributed, so
you can build a payload in pieces. A list replaces the payload outright, which is what you want for
an array response and a trap when you meant to add to a map: once a list is in place, a later map
data: fails with the same body-and-data message, naming your list as the body. Anything else, a
bare string or a number, fails with an invalid value message.
body: is a plain string. It is the right choice for CSV, HTML or file contents. On a /json/ URL
it is not used at all and the caller receives {}, because the JSON serialiser only knows how to
render a structure. Two ops both setting body: do not fail, unlike every other key here. The later
one silently replaces the earlier one.
Everything else¶
Each of the remaining keys may be set once per request. Setting the same one twice fails with a
duplicate method message, unless both values are maps, in which case they merge. headers: and
cookies: therefore split across several ops, while status: and content_type: do not.
The envelope keys are applied once the rule has finished, in a fixed order that ignores the order
you wrote them in: cookies, status, redirect, location, write, content_type, headers, header. Two
consequences follow. redirect: is applied after status: and always sets 302, so combining the
two loses the code you asked for. And headers: is applied after content_type:, so a
Content-Type inside the map wins.
Header names are rewritten to HTTP form on the way out. Every _ becomes a - and each word is
capitalised, so my_header reaches the caller as My-Header.
A value the response refuses, a map where a string was expected for instance, fails the request with an error naming the key.
header: is a dead end. The single value it carries is read as the name of a header to look up
rather than a header to set, so the entry changes nothing, and neither the designer nor the log says
so. Use headers: with a map.
write: pushes a chunk straight to the client and commits the response headers at the moment it
runs. It runs ahead of content_type:, headers: and header:, so anything those set afterwards
never reaches the caller.
A request that reaches the rule answers 200 whether the rule finished or blew up. status: and
redirect: are the only keys that change that, so an API contract that wants 4xx on an error branch
needs you to set the code by hand on that branch.
Cookie entries take the shape below. expires and max-age both accept an absolute date or a
relative offset such as +3M or +2D.
cookies:
my-cookie:
value: a cookie value
expires: +3M
path: /
domain: mydomain
secure: 1
httponly: 1
What the caller gets when the rule fails¶
A failure here, or in any other op of the rule, is not turned into an HTTP error. The status stays at
whatever the envelope keys set, 200 by default, and the caller receives a single ws_response entry
holding a Fault map with the error text, alongside _RETURN_CODE and _RETURN_TEXT values that
sit inside that payload and never reach the HTTP status line. Anything data: had collected up to
that point is discarded. The error is also written to the server log.
Clients that only check the HTTP status will read a failed request as a success. Have the caller
look for Fault in the payload, or set status: yourself on the branches that can fail.
The calling URL decides the format¶
The response format comes from the URL the caller used, not from this op:
http(s)://clariveserver/rule/[json|yaml|xml|raw]/[rule-id]. An unrecognised format behaves like
raw. A data: payload is serialised to JSON, YAML or XML accordingly, and under raw a map comes
back as YAML text. ${ws_format} in the stash tells the rule which one was asked for, so one rule
can answer differently per format.
content_type: and headers: are applied after the serialiser has set its own content type, so
they override it when you need them to.
Combining with other ops¶
Guard the response so each branch answers once. IF var condition THEN picks between a success and an error response, and ELSE carries the other half. Two of these ops firing on the same request is the most common source of the duplicate-key failure.
Gather the values first. SET VAR and
EVAL JavaScript build the structures that data: then references
through ${var}.
Web Request is the other side of the same coin: it calls a webservice, this op answers one.
Examples¶
A plain acknowledgement.
Response:
status: 200
data:
status: OK
An error response driven by a stash variable, sent from inside a conditional branch.
Response:
status: 404
data:
errors: ${foo_errors}
A CSV download. body: carries the text and the two envelope keys make the browser save it.
Response:
content_type: text/csv
headers:
Content-Disposition: attachment;filename=example.csv
body: |
name,count
bar,12
baz,7