title | layout | category |
---|---|---|
Kettle Request Handlers and the kettle.app grade |
default |
Kettle |
A kettle.server
comprises one or more kettle.app
units, each of which comprises an independently
mountable application unit. Within a kettle.app
, each
type of request handled by the application is defined using a
kettle.request
component.
Request handlers are registered in the requestHandlers
section of the options of a kettle.app
– see the
sample app for positioning of this component in the
containment structure. This consists of a free hash of handlerName
strings to handlerRecord
structures.
{
<handlerName> : <handlerRecord>,
<handlerName> : <handlerRecord>,
...
}
Note that the handlerName
s are simply free strings and have no function other than to uniquely name the handler in the
context of its app. These strings exist to allow easy alignment when
multiple apps are merged together from different sources to produce combined apps.
Members of an handlerRecord entry within the requestHandlers block
of a kettle.app component
|
||
---|---|---|
Member | Type | Description |
type |
String |
The name of a request handling grade, which must be descended from kettle.request . If you
supply the method field, your grade must be descended from kettle.request.http .
|
route |
String |
An express-compatible routing string, expressing the range of HTTP paths to be handled by this handler, together with any named parameters and query parameters that should be captured. The exact syntax for route matching is documented more precisely at pillarjs. |
gradeNames (optional) |
String/Array of String |
One or more grade names which will be mixed in to the constructed handler when it is constructed. |
prefix (optional) |
String |
A routing prefix to be prepended to this handler's route . The prefix plus the route
expression must match the incoming request in order for this handler to be activated –
but if it is, it will only see the portion of the URL matched by route in the member
request.req.url . The entire incoming URL will remain visible in
request.req.originalUrl –
this is the same behaviour as express.js routing system.
It is primarily useful when using
static middleware
which will compare the req.url value with the filesystem path relative to its mount point.
|
method (optional) |
String value – one of the valid
HTTP methods
supported by node.js, expressed in lower case, or else a comma-separated sequence of such values.
|
The HTTP request type(s) which this handler will match. method is omitted in the
case that the request handling grade is not descended from kettle.request.http – the only
currently supported requests of that type are WebSockets requests descended from kettle.request.ws .
|
A handler for a particular request must have a
grade
registered with Infusion whose name matches the type
field in the handlerRecord
structure just described. The parent
grades of this grade must be consistent with the the request you expect to handle descended from kettle.request.http
in the case of an HTTP request, or kettle.request.ws
in the case of a WebSockets request. In addition, the grade must
define (at minimum) an invoker named handleRequest
.
This invoker will be called by Kettle when your route is matched, and be supplied a single argument holding the
request object, an object whose grade is your request handler's grade, which the framework has
constructed to handle the request.
We duplicate the definitions from the sample application in order to show a minimal request handler grade and request handler function:
fluid.defaults("examples.simpleConfig.handler", {
gradeNames: "kettle.request.http",
invokers: {
handleRequest: "examples.simpleConfig.handleRequest"
}
});
examples.simpleConfig.handleRequest = function (request) {
request.events.onSuccess.fire({
message: "GET request received on path /handlerPath"
});
};
In the next section we will talk more about request (handler) objects, the members you can expect on them, and how to use them.
A request component is constructed by Kettle when it has determined the correct
handler record which matches the incoming request. This request
component will be usefully populated with material drawn from the request and node.js initial process of handling it.
It also contains various elements supplied by Kettle in order to support you in handling the request. You can add any
further material that you like to the request object by adding entries to its grade definition, of any of the types
supported by Infusion's
component configuration options.
Here we will document the standard members that are placed there by Kettle for the two standard request types which are
supported, kettle.request.http
and kettle.request.ws
. These are derived from a common grade kettle.request
which
defines several common members and workflow elements.
The following table describes the members defined on kettle.request.http
. Where these are not listed as "only for
kettle.request.http
" they are defined in the
base grade kettle.request
and so are also available for WebSockets components of type kettle.request.ws
.
Members defined by default at top-level on an HTTP request component of type
kettle.request.http |
||
---|---|---|
Member | Type | Description |
req |
http.IncomingMessage |
The request object produced by node.js – this is the value which is commonly referred to as
req in the standard express
middleware pattern |
res (only for kettle.request.http ) |
http.ServerResponse |
The response object produced by node.js – this is the value which is commonly referred to as
res in the standard express
middleware pattern
|
events.onSuccess (only for kettle.request.http ) |
Event |
A standard Infusion
Event which should be fired if the request is to produce a response successfully. The event argument
will produce the response body – if it is of type Object , it will be JSON-encoded.
|
events.onError |
Event
|
A standard Infusion
Event which should be fired if the request is to send an error response.
For a request of type kettle.request.http , the argument to the event must be an
Object with at least
a field message of type String holding the error message to be returned to the
client. The argument can also include a member statusCode of type Number
holding the HTTP status code to accompany the error if this is not supplied, it will default to 500.
|
handlerPromise (only for kettle.request.http ) |
Promise |
This promise is a proxy for the two events onSuccess and onError , packaged as
a Promise. This promise exposes methods resolve
and reject which forward their arguments to onSuccess and onError
respectively. In addition, the promise exposes a then method which accepts two callbacks
which can be used to listen to these event firings respectively. Note that this promise is not compliant
with any particular specification for promises, including ES6, A+, etc. – in the language of those
specifications, it is simply a thenable which also includes the standard resolution methods
resolve and reject . Implementation at
FluidPromises.js.
|
Note that, conversely with the req
property of the Kettle request component, the Kettle request component itself will
be marked onto the node.js request object so that it can easily be retrieved from standard middleware, etc. – it will
be available as req.fluidRequest
where req
is the request object described in the table above. More details follow
on middleware in the section working with middleware.
WebSockets communications in a Kettle application are mediated by the ws WebSockets
library – you should get familiar with the documentation for that library if you intend to use this functionality
significantly. It is also worth spending some time familiarising yourself with at least some of the ws
implementation
code since there are several aspects not properly covered by the documentation.
The request component for a WebSockets request, derived from the grade kettle.request.ws
, includes the members in the
above table which are not marked as kettle.request.http
only via kettle.request
, as well as several more members
described in the following table:
Members defined by default at top-level on a WebSockets request component of type
kettle.request.ws |
||
---|---|---|
Member | Type | Description |
events.onBindWs |
Event |
A standard Infusion
Event which is fired by the framework when the original HTTP connection has completed the
handshake and upgrade sequence
and the
ws.WebSocket
object has been allocated. Any listener registered to this event will receive two arguments - firstly,
the kettle.request.ws component itself, and secondly the
ws.WebSocket
object.
|
ws |
ws.WebSocket |
The ws.WebSocket advertised by the ws
WebSockets library as allocated to handle one end of an established WebSockets connection. This will be
of the variety referred to in the ws docs as "a WebSocket constructed by a Server".
This member will only be present after the onBindWs event described in the previous row has
fired.
|
sendMessage |
Function(message: Any) |
The sendMessage method is used to sent a WebSocket message to the client. By default, the
sendMessageJSON options described in the table below is set to true and so
sendMessage will accept a JavaScript object which will be encoded as JSON.
|
sendTypedMessage |
Function(type: String, payload: Object) |
Operates a simple "typed message" system which will fire a payload to sendMessage
consisting of {type: type, payload: payload} . This method is only useful together with the
sendMessageJSON: true option (its default value).
|
events.onReceiveMessage |
Event |
A standard Infusion
Event which is fired by the framework when a message is received from the client at the other end of
the WebSockets connection. The arguments to the event are (that, message) where
that represents this request component itself, and message represnts
the message sent by the client. If the receiveMessageJSON option is set to
true for this component (the default), the message will have been decoded as JSON.
|
events.onSendMessage |
Event |
A standard Infusion
Event which operates the workflow started by the sendMessage method.
This is a transforming promise chain event
for which each listener has the chance to transform the payload before it is sent to the next. More
information is available for the
fireTransformEvent function from Infusion's Promises API
|
events.onError |
Event |
A standard Infusion
Event which should be fired if the request is to send an error response.
This event has the same name as the one fired by a kettle.request.http but the behaviour
and semantic is different. Rather than sending an HTTP error response, the framework instead
emits a WebSockets event of type error . Because of this, the statusCode field
of the event argument should not be used. However, it is recommended that the event payload still
includes a field message of type String holding the error message to be
returned to the client, as well as a boolean member isError with the value
true .
|
A kettle.request.ws
accepts the following configurable options at top level:
Supported configurable options for a kettle.request.ws |
||
---|---|---|
Option | Type | Description |
sendMessageJSON |
Boolean (default: true ) |
If this is set to true , the argument supplied to the component's sendMessage
method will be encoded as JSON. Otherwise the argument will be sent to websocket.send
as is.
|
receiveMessageJSON |
Boolean (default: true ) |
If this is set to true , the argument received by listeners to the component's
onReceiveMessage event will be encoded as JSON. Otherwise the value will be transmitted as
from the WebSocket's message event unchanged.
|
The currently recommended scheme for terminating a WebSockets conversation managed by a kettle.request.ws
component
is to call the standard close
method on the top-level ws
member. This accepts two optional arguments - a
WebSockets status code and a description message. After
processing the connection termination sequence, the request component will be automatically destroyed by the framework.
The following example shows a minimal Kettle application which hosts a single WebSockets request handler named
webSocketsHandler
at the path /webSocketsPath
. This handler
simply logs any messages received to the console.
fluid.defaults("examples.webSocketsConfig", {
gradeNames: "fluid.component",
components: {
server: {
type: "kettle.server",
options: {
gradeNames: ["kettle.server.ws"],
port: 8081,
components: {
app: {
type: "kettle.app",
options: {
requestHandlers: {
webSocketsHandler: {
"type": "examples.webSocketsConfig.handler",
"route": "/webSocketsPath"
}
}
}
}
}
}
}
}
});
fluid.defaults("examples.webSocketsConfig.handler", {
gradeNames: "kettle.request.ws",
listeners: {
onReceiveMessage: "examples.webSocketsConfig.receiveMessage"
}
});
examples.webSocketsConfig.receiveMessage = function (request, message) {
console.log("Received WebSockets message " + JSON.stringify(message, null, 2));
};
// Construct the server using the above config
examples.webSocketsConfig();