title | layout | category |
---|---|---|
Kettle Request Handlers and the kettle.app grade |
default |
Kettle |
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.
###Structure of the requestHandlers
option of a kettle.app
{
<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.
<tr>
<td><code>method</code> (optional)</td>
<td><code>String</code> value – one of the valid <a href="https://github.com/nodejs/node/blob/master/deps/http_parser/http_parser.h#L88">HTTP methods</a> supported by node.js, expressed in lower case, or else a comma-separated
sequence of such values.
</td>
<td>The HTTP request type(s) which this handler will match. <code>method</code> is omitted in the
case that the request handling grade is not descended from <code>kettle.request.http</code> – the only currently supported requests of that type are WebSockets requests descended from <code>kettle.request.ws</code>
</tr>
</tbody>
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 |
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.
|
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: {
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();