Froute
Routing library taking advantage of the MOP
Install / Use
/learn @StephenWakely/FrouteREADME
- Froute - An Http routing class that takes advantage of the MOP
Froute is an Http routing metaclass that uses the Metaobject Protocol that lets you take advantage of CLOS to flexibly build up your Http routes.
Currently works with Hunchentoot, but it is designed to be straightforward to work with other web servers, once the adaptors are developed.
Froute has been tested on
- SBCL
- CCL
but should hopefully work on any lisp that supports the Metaobject Protocol.
- Installing
Load froute.asd and then run
#+BEGIN_SRC lisp (ql:quickload 'froute) (ql:quickload 'froute/hunchentoot) #+END_SRC
If you want to test the library
#+BEGIN_SRC lisp (ql:quickload 'froute/test) (asdf:operate 'asdf:test-op 'froute) #+END_SRC
- Defining a route
To define a route, define a class with a froute-class metaclass and include a :route property to indicate the path the route is invoked with.
#+BEGIN_SRC lisp
(defclass shporgle () () (:metaclass froute:froute-class) (:route "/shporgle"))
#+END_SRC
#+RESULTS: : #<FROUTE-CLASS FROUTE::SHPORGLE>
When the route is invoked a run method is called with an instance of this class together with the method.
#+BEGIN_SRC lisp
(defmethod froute:run ((r shporgle) (method (eql :get))) (setf (hunchentoot:content-type*) "text/html") "<html><h1>Shporgle!</h1></html>")
#+END_SRC
#+RESULTS: : #<STANDARD-METHOD FROUTE:RUN (SHPORGLE (EQL :GET)) {1006536BA3}>
An acceptor is provided to interface between Froute and Hunchentoot. To set it up call the following :
#+BEGIN_SRC lisp (defvar app nil)
(defun start-server () (setf app (make-instance 'froute-hunchentoot:froute-acceptor :port 4343)) (hunchentoot:start app))
;; Start the server (start-server)
#+END_SRC
-
Parameters
You can insert parameters into the route. The values of the parameters will be inserted into the slots of the invoked class.
#+BEGIN_SRC lisp (defclass goblins () ((id :reader goblin-id)) (:metaclass froute:froute-class) (:route "/goblin/:id"))
(defmethod froute:run ((g goblins) method) (setf (hunchentoot:content-type*) "text/html") (format nil "<h1>I am goblin ~A</h1>" (goblin-id g)))
#+END_SRC
#+RESULTS: : #<STANDARD-METHOD FROUTE:RUN (GOBLINS T) {1006ABA323}>
#+BEGIN_SRC % curl localhost:4343/goblin/groove
<h1>I am goblin groove</h1>% #+END_SRCIf you want the parameter to consume the rest of the route, append it with an asterix. This is useful for when you want to create the handler for your static files :
#+BEGIN_SRC lisp
(defclass static-handler () ((path :accessor handler-path)) (:metaclass froute:froute-class) (:route "/public/:path*"))
(defmethod froute:run ((r static-handler) (method (eql :get))) (hunchentoot:handle-static-file (resource-path (format nil "assets/~A" (handler-path r)))))
#+END_SRC
- Using CLOS
You can use inheritance to build up your route :
#+BEGIN_SRC lisp (defclass api () ((api :reader api)) (:metaclass froute:froute-class) (:route "/api/:api"))
(defclass norgle (api) ((id :reader norgle-id)) (:metaclass froute:froute-class) (:route "/norgle/:id"))
(defmethod froute:run ((n norgle) method) (format nil "Norgle ~A from api ~A" (norgle-id n) (api n)))
#+END_SRC
#+RESULTS: : #<STANDARD-METHOD FROUTE:RUN (NORGLE T) {1002EA0B43}>
#+BEGIN_SRC % curl localhost:4343/api/onk/norgle/splorge Norgle splorge from api onk #+END_SRC
Note that if you have a slot with the same name as a slot in a class you inherit from, they are the same slot. So you should ensure you keep your slot name distinct.
You can inherit from classes that do not have froute-class as a metaclass. They do not affect the route, but you can use these classes to wrap the request handling.
Say you had a page that required authentication headers.
#+BEGIN_SRC lisp
(defclass require-authentication () ())
(defmethod run :around ((r require-authentication) method) (multiple-value-bind (user password) (hunchentoot:authorization) (if (and (string= user "headgoblin") (string= password "s3cr3t")) (call-next-method) "Access Denied")))
(defclass goblins (require-authentication) () (:metaclass froute:froute-class) (:route "/goblins"))
(defmethod run ((r goblins) method) "Hurrah")
#+END_SRC
#+RESULTS: : #<COMMON-LISP:STANDARD-METHOD RUN (GOBLINS T)>
When the route inherits from require-authentication it implicitly requires the authentication check before it will be invoked.
#+BEGIN_SRC
% curl localhost:4343/goblins
Access Denied%
% curl --user headgoblin:s3cr3t localhost:4343/goblins
Hurrah%
#+END_SRC
Related Skills
node-connect
346.8kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
107.6kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
346.8kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
346.8kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
