index.js in WebApps
index.js is the entry point and route configuration for a WebApp. The concept is influenced by Express.js. index.js is executed server side, which means Sitevision's Public API can be accessed, while browser objects cannot be accessed.
A simple index.js:
(function() {
'use strict';
var router = require('router');
router.get('/', function(req, res) {
res.render('/', {});
});
}());
Routing
Routes handle how a WebApp responds to client requests. All WebApps have a unique segment in the URL where routing information is kept. WebApp routes differ from application routes since WebApp routes are local and should be used to keep state for a single WebApp. The router object provides methods to respond to the following request methods:
- GET
- PUT
- POST
- DELETE
- PATCH (@since 2023.02.1)
A route definition has the following structure:
router.method(path, handler);
Basic examples:
var router = require('router'); // retrieve an instance of the router object
// respond to a GET-request to the root route ('/')
router.get('/', function(req, res) {
res.send('Hello from my WebApp!');
});
// respond to a GET-request to '/user'
router.get('/user', function(req, res){
res.send('Hello from user route');
});
Middleware [@since 5.1]
A middleware function is a function that have access to the request object (req
), response object (res
) and a function (next
) triggering the next function in the request-response chain. The next function will either be another middleware or the actual route function. Middleware functions are executed every time the app receives a request matching a route.
Middleware functions are registered using the router.use
-method:
var router = require('router');
// register a middleware that will be executed every time the app recives a request
router.use((req, res, next) => {
if (req.method === 'PUT') {
// do something
}
// trigger next function in the stack (next middleware or route function)
next();
});
router.get('/', (req, res) => {
res.send('Hello');
});
Requests may be terminated and altered within a middleware. Keep in mind that the next()
-function must be called in order to pass over control to the next function in the stack.
Multiple middleware functions may be registered and they will be executed in the order they are registered, i.e. top-down.
const router = require('router'),
logUtil = require('LogUtil');
router.use((req, res, next) => {
logUtil.info('Hello from first');
next();
});
router.use((req, res, next) => {
logUtil.info('Hello from second');
next();
});
router.get('/', (req, res) => {
logUtil.info('Hello from route');
res.send('Hello');
});
// => 'Hello from first'
// => 'Hello from second'
// => 'Hello from route'
Route parameters
Route parameters are used to capture a value in the URL. Below is an example of a path that will capture id
on the /user
route.
// GET /user/123
router.get('/user/:id', function(req, res) {
// route is matched and {id: 123} will be populated in the req.params object
});
Router methods
router.getUrl(path [, queryStringParameters]) [@since 8.0]
Returns a URL given a path. Provide queryStringParameters
as an object to include query string parameters. The URL will match configured paths in index.js.
router.getStandaloneUrl(path [, queryStringParameters]) [@since 8.0]
Returns a URL given a path. This type or URL is used to execute a route standalone, i.e. it will not be part of page rendering. This is very useful when you only want to target your route without any side effects. The SDK documentation contains example of client-side usage.
Request object (req)
The req
object is a representation of the HTTP-request.
Properties
req.params
Object that contains parameters passed to a route. The object contains route parameters ('/user/:id
'), query string parameters and values in the request body from POST
, PUT
and DELETE
.
// GET /user/456
router.get('/user/:id', function(req, res) {
logger.info(req.params.id); // 456
});
// GET /?foo=bar
router.get('/', function(req, res){
logger.info(req.params.foo); // bar
});
// POST /addToBasket (productId=1)
router.post('/addToBasket', function(req, res){
logger.info(req.params.productId); // 1
});
req.cookies
Object that contains cookies from the request.
// Cookie basketId=789
router.get('/', function(req, res){
logger.info(req.cookies.basketId); // 789
});
req.xhr
Boolean indicating whether or not the request is an XHR (ajax) request.
router.get('/', function(req, res) {
if (req.xhr) {
return res.json({foo: 'bar'});
}
return res.render('/', {foo: 'bar'});
});
req.session
Session data is stored and accessed through the req.session
property. Session attributes are namespaced for WebApps/RESTapps, i.e. "global" session attributes cannot be accessed. Note that session data must be JSON serializable.
// WebApp/RESTApp x
router.get('/', function(req, res) {
req.session.name = 'John Doe';
});
// WebApp/RESTApp y
router.get('/', function(req, res) {
logger.info(req.session.name); // 'John Doe';
});
req.hostname [@since 4.5.3]
Hostname from the HTTP request.
router.post('/', function(req, res) {
logger.info(req.hostname); //'developer.sitevision.se'
});
req.protocol [@since 5.0]
Protocol from the request (http or https).
router.get('/', (req, res) => {
logger.info(req.protocol); //'https'
});
req.secure [@since 5.0]
Boolean shorthand checking req.protocol === 'https'
.
router.get('/', (req, res) => {
if (req.secure) {
// ...
}
});
If a load balancer or proxy is used, make sure that the client request protocol is forwarded to Sitevision.
req.method [@since 5.1]
HTTP method of the request.
router.use((req, res, next) => {
if (req.method === 'PUT') {
return res.status(403);
}
next();
});
req.path [@since 6.2]
Contains the path part of the request URL (the string used to resolve a route).
// example.com/appresource/4.x/12.x/users
router.get('/users', (req, res) => {
logger.info(req.path); // '/users'
});
req.uri [@since 2023.04.1]
The client request uri (the uri that triggered rendering of this app). Note! The uri is not always equal to "the uri of current page".
// example.com/anything/something
router.get('/', (req, res) => {
logger.info(req.uri); // '/anything/something'
});
req.serverside [@since 2023.08.1]
Whether the request is a server-side one or not. State is true when:
- The WebApp is rendered via
OutputUtil
- The Sitevision indexing process renders the WebApp (Indexer user)
- The Sitevision HTML extraction ("Web archiving") process renders the WebApp (typically the Extractor user)
router.get('/', (req, res) => {
if (req.serverside) {
// ...
}
});
Methods
req.invalidateSession()
Invalidates current session.
req.updateSession() [@since 2023.03.1]
Re-synchronizes local req.session
state with the global/shared App session state. Typically needed when multiple apps are executed intertwined
req.file(fileParamaterName) [@since 4.5.4]
Method to retrieve files from a multipart request. Files are exposed as sv:temporaryFile-nodes.
FileUtil and ImageUtil provide useful methods when working with temporary file nodes.
router.post('/upload', function(req, res) {
var file = req.file('file');
fileUtil.createFileFromTemporary(resourceLocatorUtil.getFileRepository(), file);
res.render('/', {});
});
req.header(name) [@since 4.5.2]
Request headers from the HTTP-request
router.post('/', function(req, res) {
logger.info(req.header('user-agent')); //mozilla....
});
Response object (res
)
The res
object is a representation of the HTTP-response.
Methods
res.set(name, value)
Sets a HTTP-header in the response. Returns this
for chaining purposes.
res.set('Cache-Control', 'no-cache')
.set('X-Content-Type-Options', 'nosniff');
res.type(type) [@since 4.5.2]
Sets the response Content-Type
header. If type contains the “/” character, then it will be used as-is. Returns this
for chaining purposes.
res.type('.html'); // => 'text/html'
res.type('html'); // => 'text/html'
res.type('text/html'); // => 'text/html'
res.type('svg'); // => 'image/svg+xml'
res.type('txt'); // => 'text/plain'
res.send(response)
Sends a character response. Type will be text/html
if not explicitly set.
res.send('<p>Hello from WebApp!</p>');
res.type('svg')
.send('<svg version="1.1" ... </svg>');
res.json(response)
Sends a application/json
-response.
res.json({
id: '123',
foo: 'bar'
});
res.sendFile(file)
Sends a file as response. Uses best effort for content type matching if no type is explicitly set. File argument must be a Sitevision JCR-Node of type sv:file
, sv:image
or sv:temporaryFile
. Byte array (byte[]
) is also valid as of Sitevision 5.0.1 (java.io.File is also allowed for legacy reasons).
res.sendFile(file);
res.type('application/xml; charset=ISO-8859-1')
.sendFile(file);
Target your route with a standalone url when delivering files
res.status(code)
Sets the HTTP-status for the response. Returns this
for chaining purposes.
res.status(404)
.send('Not found');
res.render(route, data)
Renders the WebApp. Rendering will always start from the main component. The route parameter will be passed to the data
object, which will be the initial state for the WebApp's store.
router.get('/', function(req, res) {
var data = {
entries: [],
buttonText: appData.get('buttonText')
};
res.render('/', data);
});
router.get('/search', function(req, res) {
var data = {
entries: [],
buttonText: appData.get('buttonText')
};
res.render('/search', data);
});
res.redirect(path [, queryStringObject] [, status]) [@since 4.5.3]
res.redirect(path [, status]) pre 4.5.3
Redirects to a path
with a specified status. If a status
is missing, it defaults to 302. A "back" redirection redirects the request back to the referer, defaulting to / when the Referer header is missing. A ".." redirection redirects to the relative path above current.
The queryStringObject
argument will be converted and added as query string paramater.
Note! Redirecting does not work when the servlet response is committed, i.e. when rendering has started.
router.post('/addToBasket', function(req, res) {
...
if (req.xhr) {
return res.json(json);
}
// redirects back to referer
return res.redirect('back');
});
router.post('/addToBasket', function(req, res) {
// redirects to the URL derived from the /search path
return res.redirect('/search');
});
router.post('/addToBasket', function(req, res) {
// redirect with query string
return res.redirect('back', {foo: 'bar'}); //url?foo=bar
});
Use a hook or target your route with a standalone url if you want to redirect
res.cookie(cookie)
Adds a cookie. The cookie
parameter is an object that can have the following properties.
Note! Setting cookies does not work when the servlet response is committed, i.e. when rendering has started.
Property | Type | Description | Default | Since |
---|---|---|---|---|
| String | The name of the cookie | ||
| String | The value of the cookie | ||
| Boolean | Flag to prevent client side scripts from accessing the cookie | false | 5.0 |
| Boolean | Sets the cookie to be used with HTTPS only | false | 5.0 |
| Number | The maximum age of the cookie in seconds.
| -1 | 5.0 |
| String | Determines in what contexts this cookie will be available/sent. Valid values (case-insensitive) are:
Invalid values will be treated as "no value is set by the app" (i.e. no same site attribute will be added to the cookie) | 7.1 |
res.cookie({
name: 'basketId',
value: '123',
httpOnly: true,
secure: true,
maxAge: 3600,
sameSite: 'Strict'
});
Use a hook or target your route with a standalone url if you want set cookies
res.clearCookie(name [, path])
Clears a cookie given a name. If path is missing, it defaults to '/'.
res.clearCookie('basketId');