A very simple Java-based web framework.
At this point, this project is a toy for experimenting with building a very basic web framework.
- Simple routing using a simple syntax
- Controllers are just regular objects with methods that can take a number of different types of arguments
- Routes direct requests to the controller and controller method
- Learn more about how to use Ivy properly
- More sample code and documentation
There are a few ways to run this framework:
- As a Web Application - Use
HttpDispatcherServlet
as your servlet. Any servlet init params will be used as the starting point for the Params - Within Jetty - Use
SimpleWebHandler
as your Handler. You will need to pass aMap<String, String>
into the constructor which is the basis for the Params - Using the built-in embedded Jetty webserver - Run
SimpleWebServer
. The initial Params are parsed from the command line, using anything that matches the formatkey=value
.
Controllers implement the ControllerBase
interface. Actions within a Controller are regular methods. Actions must return an object implementing the Response
interface. By default, all methods returning a Response
object are potential actions, however this can be altered by adding the @ControllerOptions
annotation to the Controller class, setting exposeAll=false
and then tagging the methods to be exposed with the @WebAction
annotation. Controller can have Params injected when constructed (which is the combination of the Application's initialization parameters, the initial configuraiton's params
element, and the Controller's `params' element).
Actions may take a number of possible parameters, and the values for these parameters are automatically populated based on the parameter's type:
HttpServletRequest
- The request'sHttpServletRequest
HttpServletResponse
- the request'sHttpServletResponse
String
- The full path that was matchedMap
- The current state of the Params (everything built up during the application's initialization, plus any new values from the processing of the route)
One of the central themes of this framework is that there is a "Parameter Map" ("Params" for short) that is created when the framework is first initialized, and built up (new items added, possibly overwriting previous ones) as different parts of the application are initialized and as routes are matched.
Params are built up (in order) from the following places:
- Application initialization (as described above)
- The initial configuration's
params
element (which is a JSON map) - The matched controller instance's params
- A route's attributes (every key defined as part of a route)
- Variables matched as part of a route (:id, :name, etc...)
The framework's initial configuration is loaded from the classpath, from a file named by the Param config.file
, but if that isn't specified, it defaults to /config.json
. If this file is not found, the bootstrap configuration in /basic-config.json
is used. This initial configuration specifies the configClass
(implementing the Configuration
interface) to use for the next phase of intialization, as well as an optional params
map which is added to Params. The Configuration
object can have any values from Params
injected (by name) automatically (with conversions to Integer
/int
, Boolean
/bool
if necessary). Additionally, if there is a property params
with a single Map
as its parameter (in other words a method setParams(Map)
), the Params will be injected there.
{
configClass: 'com.internetitem.simpleweb.config.BasicConfiguration',
params: {
routes: 'routes.json'
}
}
The default BasicConfiguration
expects that the `routes' Param be set to a filename (defaults to 'routes.json'). Any files with that name are loaded (in classpath order) to add routes.
Each route maps a set of URLs to a named method ("action") within a controller. Each controller class is defined in the router's configuration, and is then referenced by name within routes.
Routes are checked in the order that they were defined, and the first matching route wins. All attributes of the route are added to Params for that request, followed by any labeled matches within the route.
The following Params have special meanings to the routing process, and are used as part of routing decisions:
pattern
- The pattern to match (described below)controller
- The name of the controller to use (as defined in theroutes.json
file)invalid:controller
- A "backup" controller that will be used if the originalcontroller
is not foundaction
- The method to call within the controller (defaults to "index")invalid:action
- A "backup" method that will be used if the originalaction
is not foundredirect
- If set, an HTTP Redirect is sent to the client. Note that this parameter uses expansion and has access to the current state of Params
Note that the Params listed above must be defined within the route itself and aren't inherited as part of the usual Params process (although they will be available in the final Params passed to the controller's action method).
{
controllers: [
{
name: 'test',
className: 'com.internetitem.simpleweb.test.TestController'
},
{
name: 'files',
className: 'com.internetitem.simpleweb.controllers.StaticFileController',
params: {
file: '/files/${path}',
serveFrom: 'classpath'
}
}
],
routes: [
{ pattern: '/test/?', controller: 'test', action: 'test1', which: '1' },
{ pattern: '/test/?', controller: 'test', action: 'test1', which: '2' }
}
}
- A route contains a number of "parts", each separated by slashes
- Each part can be a string of characters or a wildcard (
*
) - Strings of characters:
- May contain a label like
:id
- Any text matched in the place of the
:label
will be added to the Params - The part may end with a question mark (
?
) at the end to indicate that the part is optional. Note that once an optional part is found, all parts that follow are always optional
- May contain a label like
- The wildcard (
*
):- May only occur once in a URL
- The matched text is always optional
- Any text captured is stored in the
path
Param
- If the route ends with a slash (
/
), that slash may be followed by a question mark to indicate that the slash is optional
Serve static files from the classpath or filesystem.
Configured using the following Params:
file
- The path to be served. This is expanded with full access to the current state of Paramspath
- Iffile
is missing, this is used as the file to be served insteadserveFrom
- If set toclasspath
then files are served from the classpath. Otherwise they are served from the filesystemcontentType
- If this is set, it is used as theContent-Type
for all files served. Otherwise the content type is automatically detected using the filename
WARNING: It appears that Java treats directories as "files" when loading them from the classpath (with getResourceAsStream()
). The "contents" of this file appears to be a directory listing. This means that it may be possible to construct a URL in which a directory's contents is returned to the user.