File serving
Static files, scripts, templates, and directory listings can be invoked and served directly out of the public folder. Files that implement REST conventions should, instead, be placed in the app/resources folder.
Files and directories in the public folder can be accessed by a client directly. For example, the <appRoot>/public/world/hello.groovy file is accessible using the following address: http://host/world/hello.groovy. This article provides information about file serving.
Types of files that can be served
There are two types of files relative to file serving:
- Executable file
- A file associated with an IBM® WebSphere® sMash
Interpreter. File suffix/Interpreter associations are registered in the GlobalContext under/config/interpreters. By default, files with the.groovyand.gtfiletypes are executable files. - Static file
- Any file other than an executable file.
Executable files are interpreted and the output of that interpretation is returned; for static files, the content of the file is returned.
Executable files can be accessed using the HTTP methods GET, HEAD, PUT, POST and DELETE. Static files and directory listings can only be accessed using the GET and HEAD methods; any other method results in a 405 Method Not Allowed error.
Groovy scripts
Groovy scripts (.groovy) can be either free-form scripts or organized around specific HTTP-method handlers.
The following example shows a complete free-form script:
println "Response from a " + request.method + " request"
The following example shows a complete script with HTTP-method handlers:
def onGET() {
println "Response from a GET request"
}
def onPUT() {
println "Response from a PUT request"
}
def onPOST() {
println "Response from a POST request"
}
def onDELETE() {
println "Response from a DELETE request"
}
// For example, fail the request for unsupported methods
request.status = 405
WebSphere sMash first attempts to invoke an HTTP-method handler (for example, onGET() for an HTTP GET request). If no such method is found, then the entire script is run.
PHP scripts
PHP scripts (.php) can be either free-form scripts or organized as classes around specific HTTP-method handlers.
The following example shows a complete free-form script which will be invoked for all HTTP methods GET, HEAD, PUT, POST and DELETE and which will echo back the HTTP method type:
<?php
echo "Response from a ".get('/request/method'). " request";
?>
The following example shows a complete script with HTTP-method handlers:
<?php
include "my_include.php"; //outer scope code is executed first
class Employees {
function __construct() {
// Constructor with no arguments will be called
echo "sMash:";
}
function onGET() {
echo "Response from a GET request";
}
function onPUT() {
echo "Response from a PUT request";
}
function onPOST() {
echo "Response from a POST request";
}
function onDELETE() {
echo "Response from a DELETE request";
}
}
?>
WebSphere sMash will first execute any outer scope script that may be present in the file. This is often used to run include statements.
It will then look for a PHP class defined in the runtime with a class name that matches the filename.
If a matching class is found and it contains a method with a name that matches the appropriate HTTP method handler for the HTTP method being used then it is invoked. (for example, Employees::onGET() for an HTTP GET request.
WebSphere sMash prepares to invoke the method by creating an instance of the PHP class. If there is a constructor method that takes no arguments then this will be called.
Note that the class does not actually need to be defined in the PHP file specified in the HTTP request, it can instead be defined in an includeed PHP script.
Note that PHP Class and function names are case-insensitive so whilst it is good practice to use names such as onGET in fact onget would also match.
Supported Encoding format
WebSphere sMash currently supports the UTF-8 encoding format only. If the browser is set to any other encoding format other than UTF-8, some of the APIs in WebSphere sMash, like retrieving request parameters for example, might not work correctly.
Path information and search order
For executable files, the file that is run is the first matching file that can be located by walking down the directory structure. Everything after the first matching file is considered to be path information and can be accessed using GlobalContext.zget("/event/pathInfo").
Path information is not allowed for static files, so the path the client tries to access must exactly match the file location or it is not found.
Directory listing and Default files
When a client attempts to access a directory, if there is an index.groovy file or index.gt file inside that directory, it is run and its output is returned. If neither of these files exists, but the index.html file exists, then its content is returned. If neither of these files exist, a 403 Forbidden error message is returned, unless directory browsing has been allowed. To allow directory browsing, add the following lines to your zero.config file:
The following table presents a summary of the default search order for basic WebSphere sMash applications.
| index.groovy |
| index.gt |
| index.html |
| directory listing (if enabled) or 403 |
The following table presents a summary of the default search order for PHP WebSphere sMash applications.
| index.php |
| index.html |
| directory listing (if enabled) or 403 |
Directory listings are rendered by the app/views/listing.gt template in zero.core. To override the template, do one of the following:
- Add
listing.gtinto views in your application. - Set
/config/fileserver/directoryViewto your template name.
The virtual directory name is available in the /request/directory directory and the children are in a List<File> in the /request/dirContents directory.
Serving files from dependencies
If any of the libraries your application depends on contain files in their public folders these files can be served from their virtual directories. These files are all accessible as though they physically resided in the public directory, unless there are conflicts. When a file is requested by a client the public directory of the application is searched first. If the file cannot be located there, the dependency list is searched in a breadth-first manner.
For a complete description of the search order used for finding files in dependencies, see the virtual directories section.
Caching
By default, files are served with an Etag header that can be used to validate the next request on the server. If you want to turn this behavior off set /config/fileserver/setEtag to false.
Files are also served, by default, with a Cache-Control header with a max-age of 0. You can override this behavior using the file extension. For example, to cache all .js files for 300 seconds, set the /config/fileserver/expires/.js value to 300. This serves a Cache-Control header with a max-age of 300 for all .js files.
Content negotiation
Content negotiation is a mechanism defined in the HTTP specification that makes it possible to serve different versions of a document (or more generally, a resource) at the same URI, so that user agents can specify which version fits their capabilities the best.
When enabled, WebSphere sMash provides limited support for content negotiation of static files based on the Accept-Language HTTP request header. Using the language portion for each Accept-Language value, the server orders each of the Accept-Language values based on the quality index and searches for the most appropriate match for the client making the request. If none of the languages match, the server attempts to find a match for the default language of the server, as determined by the default locale of the server.
To allow content negotiation for your application, add the following lines to your zero.config file:
The naming convention for resources leveraging content negotiation in WebSphere sMash is file name, followed by MIME-type extension, followed by a language extension. For example, the english version of the resource index.html would
be named index.html.en. The following table shows the valid hyperlinks for the naming convention used in WebSphere sMash.
| Filename | Valid hyperlink | Accept-Language | Default language |
|---|---|---|---|
| index.html.en | index.html index.html.en / (directory index) |
en | en |
| index.html.ja | index.html index.html.ja / (directory index) |
ja | en |
| index.html | index.html / (directory index) |
ja | en |
When content negotiation is used to find the most appropriate resource for the client, the server returns, as part of the HTTP response, the headers Vary, Content-Language and Content-Location.
By returning these headers, caching servers can then cache these negotiated responses more accurately. The following
table shows the response headers for a request to index.html for a client requesting english content.
| Header | value |
|---|---|
| Vary | Accept-Language |
| Content-Location | index.html.en |
| Content-Language | en |
- Content negotiation is available only for static resources and not dynamic resources such as PHP and Groovy.
- Content negotiation processing begins only when the requested resource does not exist. For example, if there is a resource named
index.htmland the client requestsindex.html, the server determines that there is no need for content negotiation since there is an exact match. - Default files can also leverage content negotiation if the default file is static and there is a default file appended with a language extension that matches the clients list of accepted languages.
- In cases where the server is running under a different locale than the default locale for the application, the application can set the default language
/config/fileserver/defaultLanguageand this value is used if there are no matches found between the client's Accept-Language header and the set of available resources that match the request.