Although you usually will build some fault tolerance into your components, on some level they expect to deal with data that has a particular structure, and which contains the expected type of information (strings, booleans, dates, etc). Although it cannot confirm whether data supplied by an end user is accurate or meaningful, the JSON Schema validation component provided by this package can at least verify whether the supplied data is "valid", i.e. that it matches the rules outlined in a particular JSON Schema.
This package provides validation middleware that can be used with either fluid-express or kettle. These validation middleware components can be used to validate incoming requests, and to automatically reject invalid payloads with a detailed error. This allows your server-side components to at least assume they will only receive JSON data that is valid according to a schema. The validation middleware is a "gatekeeper" that doesn't need to know anything about how you intend to use the data. You only need to describe the "contract", i.e. what is valid, and let the "gatekeeper" filter out incoming requests that are not valid according to your JSON Schema.
See below for usage examples for both kettle and fluid-express.
The errorBinder
component included with this package is designed to associate the validation error
messages produced by the validator with on-screen elements. See that component's documentation for details.
The base grade for validation middleware used with fluid-express. Supports all the options above, plus the options for
fluid.express.middleware
.
The incoming request is first transformed using fluid.model.transformWithRules
andoptions.rules.requestContentToValidate
. The results are validated against options.schemaKey
.
The default options validate the request body, as expected with a POST
or PUT
request. See the mix-in grades below
for examples of how different rules can handle different types of request data.
The transformed request data is validated against the schema. If there are validation errors, the validation output of this middleware is passed on to the next piece of middleware in the error-handling chain. If there are no validation errors, the next piece of middleware in non-error-handling chain is called. In both cases, this middleware does not send any kind of response itself. You are expected to ensure that middleware further along in the chain sends the response and/or sets HTTP headers.
The following component configuration options are supported:
Option | Type | Description |
---|---|---|
errorTemplate |
Object |
If there are validation errors, this object will be merged with the raw error to set the kettle-specific options like message and statusCode . |
inputSchema |
Object |
The FSS schema to use in validating incoming request data. |
rules.requestContentToValidate |
Object |
The rules to use in transforming the incoming data before validation (see below for more details). |
The default rules.requestContentToValidate
in the express middleware grade can be represented as follows:
requestContentToValidate: {
"": "body"
}
This transformation exposes only the body of the request as a top-level object to be validated. For another example,
see fluid.schema.validationMiddleware.handlesQueryData
below.
request
: An object representing the individual user's request. See thefluid-express
documentation for details.response
: The response object, which can be used to send information to the requesting user. See thefluid-express
documentation for details.next
: The next Express middleware or router function in the chain. See thefluid-express
documentation for details.- Returns: Nothing.
This invoker fulfills the standard contract for a fluid.express.middleware
component. It examines the request
content and interrupts the conversation if the JSON data supplied as part of request
is not valid according to the
JSON Schema options.schemaKey
found at options.schemaPath
. If the content is valid, execute the supplied next
function and let some other downstream piece of middleware continue the conversation.
This function is expected to be called by Express (or by an instance of fluid.express
).
A mix-in grade that configures a grade that derives from fluid.schema.validationMiddleware.base
(so, either the
fluid-express or kettle variants above) to validate query data. Sets rules.requestContentToValidate
to the following:
requestContentToValidate: {
"": "query"
}
The fluid.schema.validationMiddleware
grade is intended to be used with a fluid.express
or fluid.express.router
instance. The fluid.schema.validationMiddleware.requestAware.router
wrapper is provided as a convenient starting
point. With that router, you can created "gated" REST endpoints that only pass through valid payloads to the underlying
handlers, as shown here:
var fluid = require("infusion");
require("fluid-express");
require("fluid-json-schema");
fluid.defaults("fluid.schema.tests.handler", {
gradeNames: ["fluid.express.handler"],
invokers: {
handleRequest: {
funcName: "{that}.sendResponse",
args: [200, "Someone sent me a valid JSON payload."]
}
}
});
fluid.express({
gradeNames: ["fluid.schema.validationMiddleware.requestAware.router"],
handlerGrades: ["fluid.schema.tests.handler"],
inputSchema: {
"$schema": "fss-v7-full#",
properties: {
key: {
type: "string",
required: true
}
}
},
path: "/gatekeeper",
port: 3000
});
If you were to launch this example, you would have a REST endpoint /gatekeeper
that compares all POST request payloads
to the requestSchema. If a payload is valid according to the schema, the handler defined above would output a canned
"success" message. If the payload is invalid, the underlying fluid.express.middleware
instance steps in and responds
with a failure message.
An extension of the kettle.middleware
grade that is intended to be hosted as a child of your
kettle.app
grade, and to be
referenced as requestMiddleware
from one or more of your kettle.request.http
instances. Each validator validates
a single type of payload. See below for a usage example.
The following component configuration options are supported:
Option | Type | Description |
---|---|---|
errorTemplate |
Object |
If there are validation errors, this object will be merged with the raw error to set the kettle-specific options like message and statusCode . |
requestSchema |
Object |
The FSS schema to use in validating incoming request data. |
rules.requestContentToValidate |
Object |
The rules to use in transforming the incoming data before validation (see below for more details). |
The default rules.requestContentToValidate
in the express middleware grade can be represented as follows:
requestContentToValidate: {
"body": "body",
"params": "params",
"query": "query"
}
This transformation strips complex internal material from the underlying request and exposes only the parameters (
params
), query string data (query
) and request body (body
). There are convenience grades provided for payloads
where only the query, parameters, or body are validated. See below.
requestComponent
: Thekettle.request.http
instance fielding the actual request.- Returns: A
fluid.promise
that will be resolved if the payload is valid, or rejected with validation errors if the payload is invalid.
This invoker satisfies the basic contract for a kettle.middleware
grade. It validates a payload and returns a promise
that is resolved if processing should continue or rejected if an invalid payload is detected.
A convenience validator grade that exposes the request body as a top-level object, so that you can write simpler schemas to validate your payloads.
A convenience validator grade that exposes only the URL parameters as a top-level object, so that you can write simpler schemas to validate your payloads.
A convenience validator grade that exposes only the query string parameters as a top-level object, so that you can write simpler schemas to validate your payloads.
var fluid = require("infusion");
fluid.require("kettle");
fluid.require("fluid-json-schema");
var my = fluid.registerNamespace("my");
fluid.registerNamespace("my.kettle.handler");
my.kettle.handler.reportSuccess = function (request) {
request.events.onSuccess.fire({ message: "Payload accepted." });
};
fluid.defaults("my.kettle.validator", {
gradeNames: ["fluid.schema.kettle.validator.body"],
requestSchema: {
"$schema": "fss-v7-full#",
type: "object",
properties: {
hasBodyContent: {
type: "string",
required: true,
enum: ["good"],
enumLabels: ["Good Choice"]
}
}
}
});
// Looking for body content and validate that against our schema.
fluid.defaults("my.kettle.handler", {
gradeNames: ["kettle.request.http"],
requestMiddleware: {
validate: {
middleware: "{my.kettle.validator}"
}
},
invokers: {
handleRequest: {
funcName: "my.kettle.handler.reportSuccess"
}
}
});
fluid.defaults("my.kettle.app", {
gradeNames: ["kettle.app"],
components: {
myValidator: {
type: "my.kettle.validator"
}
},
requestHandlers: {
gatedBody: {
type: "my.kettle.handler",
route: "/gated",
method: "post"
}
}
});
my.kettle.app();
If you were to run the above example, you would have a kettle app with a /gated
POST endpoint, that would reject any
payload that does not contain a hasBodyContent
element that is specifically set to the string good
. Any payload
that contains that element with the correct value would be passed to the underlying handler stub, and result in a
"payload accepted" message.
Note that we use the fluid.schema.kettle.validator.body
grade to keep our schema simple. If we were to use the base
fluid.schema.kettle.validator
grade, our schema might look like:
{
"$schema": "fss-v7-full#",
type: "object",
properties: {
body: {
properties: {
hasBodyContent: {
type: "string",
required: true,
enum: ["good"],
enumLabels: ["Good Choice"]
}
}
}
}
}
Each of the convenience grades above allow you to avoid one layer of object-and-property nesting when you are only dealing with one type of data.