This is the easy-routes Reference Manual, generated automatically by Declt version 4.0 beta 2 "William Riker" on Sun Sep 15 05:05:40 2024 GMT+0.
The main system appears first, followed by any subsystem dependency.
easy-routes
Yet another routes handling utility on top of Hunchentoot
Mariano Montone <marianomontone@gmail.com>
MIT
# EASY-ROUTES #
EASY-ROUTES is yet another routes handling system on top of Hunchentoot.
It’s just glue code for Restas routing subsystem (CL-ROUTES).
It supports:
* Dispatch based on HTTP method
* Arguments extraction from the url path
* Decorators
* Url generation from route names
## Usage ##
Use ‘routes-acceptor‘ acceptor:
“‘lisp
(hunchentoot:start (make-instance ’easy-routes:routes-acceptor))
“‘
Note that the ‘routes-acceptor‘ returns with HTTP not found if no route matches and doesn’t fallback to ‘easy-handlers‘, and so it doesn’t iterate over Hunchentoot ‘*dispatch-table*‘. Most of the time, that iteration is a useful thing, so you may want to start the ‘easy-routes:easy-routes-acceptor‘ instead, that inherits from Hunchentoot ‘easy-acceptor‘ and so it iterates the dispatch table if no route matches (useful for being able to use ‘define-easy-handler‘ and also handling static files).
## Routes ##
### Syntax ###
“‘lisp
(defroute <name> (<path> &rest <route-options>) <route-params>
&body body)
“‘
with:
* ‘path‘: A string with an url path that can contain arguments prefixed with a colon.
Like ‘"/foo/:x/:y"‘, where ‘:x‘ and ‘:y‘ are bound into x and y variables in the context of the route body.
* ‘route-options‘: possible options are
* ‘:method‘ - The HTTP method to dispatch, as a keyword. Default is ‘:get‘.
* ‘:decorators‘ - The decorators to attach (see "decorators" section below).
* ‘:acceptor-name‘ - The name of the acceptor the route should be added to (optional).
* ‘route-params‘: a list of params to be extracted from the url or HTTP request body (POST).
Has this form: ‘(params &get get-params &post post-params &path path-params)‘, with the ‘&get‘, ‘&post‘ and ‘&path‘ params sections being optional, and where ‘params‘ are grabbed via ‘hunchentoot:parameter‘ function, ‘get-params‘ via ‘hunchentoot:get-parameter‘ function, and ‘post-params‘ via ‘hunchentoot:post-parameter‘ function. ‘path-params‘ specifies the type of params in the url path (see below for an example).
For example:
“‘lisp
(easy-routes:defroute name ("/foo/:x") (y &get z)
(format nil "x: ~a y: ~a z: ~a" x y z))
“‘
Also, params can have Hunchentoot easy-handler style options, described here: http://weitz.de/hunchentoot/#define-easy-handler
“‘
(var &key real-name parameter-type init-form request-type)
“‘
For example:
“‘lisp
(easy-routes:defroute foo "/foo/:x"
((y :real-name "Y" :init-form 22 :parameter-type ’integer))
(format nil "~A - ~A" x y))
“‘
You can also specify the type of path parameters after ‘&path‘. For example, say you want to sum a path argument to a query argument. You can specify their type as ’INTEGER and calculate their sum without parsing:
“‘lisp
(easy-routes:defroute foo "/foo/:x"
((y :init-form 10 :parameter-type ’integer)
&path (x ’integer))
(format nil "~A" (+ x y)))
“‘
### Example route ###
“‘lisp
(defroute foo ("/foo/:arg1/:arg2" :method :get
:decorators (@auth @db @html))
(&get w)
(format nil "<h1>FOO arg1: ~a arg2: ~a ~a</h1>" arg1 arg2 w))
“‘
## Url generation
Use ‘genurl‘ function with the name of the route and route parameters as keyword arguments to generate urls.
Example:
“‘lisp
(genurl ’save-payment-form :id (id application))
“‘
## Decorators ##
Decorators are functions that are executed before the route body. They should call the ‘next‘ parameter function to continue executing the decoration chain and the route body finally.
### Examples ###
“‘lisp
(defun @auth (next)
(let ((*user* (hunchentoot:session-value ’user)))
(if (not *user*)
(hunchentoot:redirect "/login")
(funcall next))))
(defun @html (next)
(setf (hunchentoot:content-type*) "text/html")
(funcall next))
(defun @json (next)
(setf (hunchentoot:content-type*) "application/json")
(funcall next))
(defun @db (next)
(postmodern:with-connection *db-spec*
(funcall next)))
“‘
Decorators also support parameters, like in the ‘@check‘ and ‘@check-permission‘ decorators:
“‘lisp
(defun @check (predicate http-error next)
(if (funcall predicate)
(funcall next)
(http-error http-error)))
(defun @check-permission (predicate next)
(if (funcall predicate)
(funcall next)
(permission-denied-error)))
“‘
Then you can use those decorators passing the needed parameters. ‘predicate‘ and ‘http-error‘ for ‘@check‘,
and ‘predicate‘ for check permission:
“‘lisp
(defroute my-protected-route ("/foo" :method :get
:decorators ((@check my-permissions-checking-function hunchentoot:+http-forbidden+)))
()
...)
“‘
## Routes for individual acceptors
By default routes are registered globally in ‘*ROUTES*‘ and ‘*ROUTES-MAPPER*‘ variables.
That is convenient for the most common case of running a single EASY-ROUTES service per Lisp image.
But it gets problematic if you want to run several EASY-ROUTES based services on the same Lisp image.
If routes are registered globally, then all your acceptors use the same routes and mapper;
that means that a service A would also respond to routes defined in service B; that’s clearly not what you want.
For that case, you can use ‘acceptor names‘ to define routes for a specific acceptor.
First you need to give your acceptor a name, using ‘:name‘ acceptor parameter:
“‘lisp
(hunchentoot:start (make-instance ’easy-routes:routes-acceptor :name ’my-service))
“‘
Then, use that name in routes definition ‘:acceptor-name‘:
“‘lisp
(defroute my-route ("/my-route" :acceptor-name my-service)
...
)
“‘
Now ‘my-route‘ is registered locally to the ‘my-service‘ acceptor; other running EASY-ROUTES acceptors don’t have it in their map anymore.
That means you can run several EASY-ROUTES acceptors at the same time on the same Lisp image now.
Lastly, you need to use ‘:acceptor-name‘ when generating urls now too:
“‘lisp
(genurl ’my-route :acceptor-name ’my-service)
“‘
## Map of routes visualization
CL-ROUTES package implement special SWANK code for routes map visualization. Just inspect ‘*ROUTES-MAP*‘ variable from your lisp listener.
For example:
“‘
#<ROUTES:MAPPER {1007630E53}>
——————–
Tree of routes
————————————————–
users invoice-engine::admin/users
api/invoices/chart invoice-engine::invoices-chart-data
invoice-engine::dashboard
logout invoice-engine::logout
company/logo invoice-engine::company-logo
search invoice-engine::global-search
preview-invoice invoice-engine::preview-invoice
dt-invoices invoice-engine::datatables-list-invoices
tenants invoice-engine::admin/tenants
admin/
settings invoice-engine::admin/settings
invoice-engine::admin/dashboard
tenants/new/
invoice-engine::admin/tenants/create
invoice-engine::admin/tenants/new
login/
invoice-engine::admin/signin
invoice-engine::admin/login
tenant/$id invoice-engine::admin/tenant
users/
new/
invoice-engine::admin/users/create
invoice-engine::admin/users/new
$id/edit/
invoice-engine::admin/users/update
invoice-engine::admin/users/edit
customers/
invoice-engine::web/list-customers
$id invoice-engine::view-customer
invoices/
invoice-engine::list-invoices-route
$id/
print invoice-engine::web/print-invoice
printed invoice-engine::web/printed-invoice
invoice-engine::view-invoice
send invoice-engine::web/send-invoice-by-email
“‘
Less fancy, but useful too, you can also use ‘(describe easy-routes:*routes-mapper*)‘ to visualize the tree of routes.
## Augmented error pages and logs
You can get augmented error pages and logs with request, session and route information, adding ‘easy-routes+errors‘ as dependency, and subclassing from ‘easy-routes-errors-acceptor‘, like:
“‘lisp
(defclass my-acceptor (easy-routes:easy-routes-acceptor easy-routes::easy-routes-errors-acceptor)
())
“‘
This is implemented with [hunchentoots-errors](https://github.com/mmontone/hunchentoot-errors) library.
## Djula integration
‘easy-routes+djula‘ system implements support for generating easy-routes urls using route names and arguments in Djula templates (calls ‘genurl‘ function).
Djula template syntax:
“‘
{% genurl route-name &rest args %}
“‘
Example:
“‘
{% genurl my-route :id 22 :foo template-var.key %}
“‘
## Reference ##
## Functions
### @html
“‘lisp
(next)
“‘
HTML decoration. Sets reply content type to text/html
### find-route
“‘lisp
(name)
“‘
Find a route by name (symbol)
### genurl
“‘lisp
(route-symbol &rest args &key &allow-other-keys)
“‘
Generate a relative url from a route name and arguments
### genurl\*
“‘lisp
(route-symbol &rest args &key &allow-other-keys)
“‘
Generate an absolute url from a route name and arguments
### redirect
“‘lisp
(route-symbol &rest args)
“‘
Redirect to a route url. Pass the route name and the parameters.
## Macros
### defroute
“‘lisp
(name template-and-options params &body body)
“‘
Route definition syntax
## Classes
### easy-routes-acceptor
This acceptor tries to match and handle easy-routes first, but fallbacks to easy-routes dispatcher if there’s no matching
### routes-acceptor
This acceptors handles routes and only routes. If no route is matched then an HTTP NOT FOUND error is returned.
If you want to use Hunchentoot easy-handlers dispatch as a fallback, use EASY-ROUTES-ACCEPTOR
hunchentoot
(system).
routes
(system).
package.lisp
(file).
util.lisp
(file).
easy-routes.lisp
(file).
routes-map-printer.lisp
(file).
Files are sorted by type and then listed depth-first from the systems components trees.
easy-routes/easy-routes.asd
easy-routes/package.lisp
easy-routes/util.lisp
easy-routes/easy-routes.lisp
easy-routes/routes-map-printer.lisp
easy-routes/util.lisp
package.lisp
(file).
easy-routes
(system).
assoc-bind
(macro).
lambda-list-split
(function).
easy-routes/easy-routes.lisp
util.lisp
(file).
easy-routes
(system).
*routes-mapper*
(special variable).
@check
(function).
@check-permission
(function).
@html
(function).
@json
(function).
acceptor-dispatch-request
(method).
acceptor-dispatch-request
(method).
defroute
(macro).
easy-routes-acceptor
(class).
easy-routes-ssl-acceptor
(class).
find-route
(function).
genurl
(function).
genurl*
(function).
http-error
(function).
not-found-error
(function).
or-http-error
(function).
or-not-found
(function).
permission-denied-error
(function).
print-object
(method).
redirect
(function).
route
(class).
route-check-conditions
(method).
route-name
(method).
routes-acceptor
(class).
*acceptors-routes-and-mappers*
(special variable).
*route*
(special variable).
*routes*
(special variable).
acceptor-routes
(function).
acceptor-routes-mapper
(function).
apply-format-aux
(function).
call-decorator
(function).
call-with-decorators
(function).
connect-routes
(function).
ensure-acceptor-routes-and-mapper
(function).
http-method
(type).
make-route-url
(method).
make-route-url
(method).
make-route-url
(method).
parse-host-and-port
(function).
process-route
(generic function).
required-method
(reader method).
route-decorators
(reader method).
route-symbol
(reader method).
route-symbol-template
(function).
valid-route-method-p
(function).
variables
(reader method).
easy-routes/routes-map-printer.lisp
easy-routes.lisp
(file).
easy-routes
(system).
*current-indention*
(special variable).
*indention*
(special variable).
print-route
(generic function).
Packages are listed by definition order.
easy-routes
common-lisp
.
*routes-mapper*
(special variable).
@check
(function).
@check-permission
(function).
@html
(function).
@json
(function).
defroute
(macro).
easy-routes-acceptor
(class).
easy-routes-ssl-acceptor
(class).
find-route
(function).
genurl
(function).
genurl*
(function).
http-error
(function).
not-found-error
(function).
or-http-error
(function).
or-not-found
(function).
permission-denied-error
(function).
redirect
(function).
route
(class).
routes-acceptor
(class).
*acceptors-routes-and-mappers*
(special variable).
*current-indention*
(special variable).
*indention*
(special variable).
*route*
(special variable).
*routes*
(special variable).
acceptor-routes
(function).
acceptor-routes-mapper
(function).
apply-format-aux
(function).
assoc-bind
(macro).
call-decorator
(function).
call-with-decorators
(function).
connect-routes
(function).
ensure-acceptor-routes-and-mapper
(function).
http-method
(type).
lambda-list-split
(function).
make-route-url
(generic function).
parse-host-and-port
(function).
print-route
(generic function).
process-route
(generic function).
required-method
(generic reader).
route-decorators
(generic reader).
route-symbol
(generic reader).
route-symbol-template
(function).
valid-route-method-p
(function).
variables
(generic reader).
Definitions are sorted by export status, category, package, and then by lexicographic order.
This is the cl-routes map of routes.
cl-routes implements special SWANK inspect code and prints routes mappers as a tree.
Just inspect *routes-mapper* from the Lisp listener to see.
Macro for defining a route.
Syntax:
(defroute <name> (<path> &rest <route-options>) <route-params>
&body body)
with:
* path: A string or a symbol evaluating to a string with an url path
that can contain arguments prefixed with a colon.
Like "/foo/:x/:y", where :x and :y are bound into x and y variables in the context of the route body.
* route-options: possible options are
* :method - The HTTP method to dispatch, as a keyword. Default is :get.
* :decorators - The decorators to attach.
* :acceptor-name - The name of the acceptor the route should be added to (optional).
* route-params: a list of params to be extracted from the url or HTTP request body (POST).
Has this form: (params &get get-params &post post-params &path path-params), with the &get, &post and &path params sections being optional, and where params are grabbed via HUNCHENTOOT:PARAMETER function, get-params via HUNCHENTOOT:GET-PARAMETER function, and post-params via HUNCHENTOOT:POST-PARAMETER function. path-params specifies the type of params in the url path.
For example:
(easy-routes:defroute name ("/foo/:x") (y &get z)
(format nil "x: ~a y: ~a z: ~a x y z))
Also, params can have Hunchentoot easy-handler style options, described here: http://weitz.de/hunchentoot/#define-easy-handler
(var &key real-name parameter-type init-form request-type)
For example:
(easy-routes:defroute foo "/foo/:x"
((y :real-name "Y" :init-form 22 :parameter-type ’integer))
(format nil "~A - ~A" x y))
You can also specify the type of path parameters after &path. For example, say you want to sum a path argument to a query argument. You can specify their type as ’INTEGER and calculate their sum without parsing:
(easy-routes:defroute foo "/foo/:x"
((y :init-form 10 :parameter-type ’integer)
&path (x ’integer))
(format nil "~A" (+ x y)))
Decorator that checks if PREDICATE evaluation is true.
PREDICATE is a funcallable object.
If the check succeeds, then the NEXT middleware is called.
If the check fails, then the request is aborted with HTTP status HTTP-ERROR.
Example usage:
(defroute my-route ("/my-route" :method :get
:decorators ((@check my-permissions-checking-function hunchentoot:+http-forbidden+)))
...
)
Decorator that aborts the current request with a HTTP permission denied error if the evaluation of PREDICATE is false. PREDICATE is a funcallable object.
HTML decoration. Sets reply content type to text/html
JSON decoration. Sets reply content type to application/json
Find a route by name (symbol)
Generate a relative url from a route name and arguments
Generate an absolute url from a route name and arguments.
Looks at HUNCHENTOOT:*REQUEST* and HUNCHENTOOT:*ACCEPTOR* to infer host and uri scheme. If HUNCHENTOOT:*REQUEST* and HUNCHENTOOT:*ACCEPTOR* are not bound, then "http" and "localhost" are used as uri scheme and host.
Abort current handler and signal HTTP error HTTP-ERROR.
HTTP-ERROR should be an HTTP status code (integer).
Aborts current handler and returns with an HTTP not found error.
Utility function for signaling HTTP-ERROR if VALUE is null.
HTTP-ERROR should be an HTTP status code (integer).
Utility function for signaling an HUNCHENTOOT:+HTTP-NOT-FOUND+ error if VALUE is null.
Use in your routes like:
(let ((my-object (or-not-found (find-my-object id))))
...)
where id is a route parameter.
The route retuns an HTTP not found error if object with that id could not be found.
Aborts current handler and returns with an HTTP forbidden error.
Redirect to a route with name ROUTE-SYMBOL. ARGS is a property list with route parameters.
easy-routes-acceptor
) request) ¶hunchentoot
.
routes-acceptor
) request) ¶hunchentoot
.
This acceptor tries to match and handle easy-routes first, but fallbacks to easy-routes dispatcher if there’s no matching
easy-acceptor
.
As for EASY-ROUTES-ACCEPTOR, but works with the hunchentoot EASY-SSL-ACCEPTOR instead.
easy-routes-acceptor
.
ssl-acceptor
.
route
.
common-lisp
.
symbol
:symbol
This slot is read-only.
(satisfies easy-routes::valid-route-method-p)
:required-method
This slot is read-only.
list
:decorators
This slot is read-only.
This acceptors handles routes and only routes. If no route is matched then an HTTP NOT FOUND error is returned. If you want to use Hunchentoot easy-handlers dispatch as a fallback, use EASY-ROUTES-ACCEPTOR
acceptor
.
Routes and route mappers for individual acceptors
The current route
Parse host and port from a Host HTTP reader.
concat-template
) stream) ¶or-template
) stream) ¶cons
) stream) ¶wildcard-template
) stream) ¶variable-template
) stream) ¶Jump to: | @
A C D E F G H L M N O P R V |
---|
Jump to: | @
A C D E F G H L M N O P R V |
---|
Jump to: | *
D R S V |
---|
Jump to: | *
D R S V |
---|
Jump to: | C E F H P R S T U |
---|
Jump to: | C E F H P R S T U |
---|