aboutsummaryrefslogtreecommitdiff
path: root/example/lazybones-example.lisp
diff options
context:
space:
mode:
Diffstat (limited to 'example/lazybones-example.lisp')
-rw-r--r--example/lazybones-example.lisp129
1 files changed, 129 insertions, 0 deletions
diff --git a/example/lazybones-example.lisp b/example/lazybones-example.lisp
new file mode 100644
index 0000000..9300467
--- /dev/null
+++ b/example/lazybones-example.lisp
@@ -0,0 +1,129 @@
+(asdf:load-system "lazybones-hunchentoot")
+
+(defpackage #:lazybones-test
+ (:use #:cl)
+ (:local-nicknames (#:lzb #:lazybones))
+ (:import-from #:lazybones
+ #:defendpoint*
+ #:http-ok
+ #:http-err))
+
+(in-package :lazybones-test)
+
+
+;; First make a server and add some custom error responses
+
+(defvar *server* (lzb:create-server :port 8888))
+
+(defun custom-404 ()
+ (format nil "~a wasn't found :o~%" (lzb:request-path))) ; can use request functiosn
+
+(defun custom-403 ()
+ "You, in particular, can't do that. :P ")
+
+(defun custom-500 ()
+ "Bah. Error.")
+
+(lzb:set-canned-response *server* 404 'custom-404 "text/plain" )
+(lzb:set-canned-response *server* 403 'custom-403 "text/plain" )
+(lzb:set-canned-response *server* 500 'custom-500 "text/plain")
+
+;; PPROVISION-APP makes an app. You can supply an optional name, a symbol.
+;; In lieu of a supplied name, the name of the package is used as the app's name.
+(lzb:provision-app ()
+ :title "Lazybones Demo App"
+ :version "0.0.0"
+ :description "Just an API that defines some endpoints. These
+ endpoints aren't meant to accomplish anything. Merely testing out
+ the lazybones HTTP routing framework."
+
+ :content-type "text/plain" ; default content type of server responses.
+ :auth 'post-authorizer) ; default authorizor for requests that need it
+
+(defun post-authorizer ()
+ "Request is authorized if it contains the right TESTAPPSESSION
+ cookie. Obtain such a cookie by posting to the /login endpoint."
+ (string-equal "coolsessionbro" (lzb:request-cookie "testappsession")))
+
+;; now we install the app to the server
+(lzb:install-app *server* (lzb:app)) ; (app) is the default app for this package
+
+;; DEFENDPOINT* is a macro to define an endpoint and install it into the
+;; app whose name is the current package anme. DEFENDPOINT (without the *)
+;; allows you to explictly specify the app where the endpoint is installed.
+
+;; The general syntax is: DEFENDPOINT* HTTP-METHOD ROUTE-TEMPLATE QUERY-PARAMETERS OPTIONS DOCSTRING BODY ...
+
+(defendpoint* :post "/login" () ()
+ "Dummy login endpoint for returning a session cookie. Always returns
+ the \"true\" and sends a set-cookie header, setting 'testappsession'
+ to 'coolsessionbro'."
+ (print (lzb:request-body)) ; dummy implementation, prints post body to stdout
+ (setf (lzb:response-cookie "testappsession") "coolsessionbro")
+ (http-ok "true"))
+
+
+;; The next route defines a route variable WHO
+(defendpoint* :get "/hello/:who:" () ()
+ "Echos back Hello WHO"
+ (http-ok (format nil "Hello ~a~%" who))) ; use the route variable here
+
+(defendpoint* :post "/hello/:who:" ()
+ (:auth t) ; use the app's default authorizor
+ "Echo's back 'Hello WHO, I got your message BODY' where BODY is the post body."
+ (print (lzb:request-header :content-type))
+ (let ((body (lzb:request-body)))
+ (http-ok (format nil "Hello ~a, I got your message ~a~%"
+ who body))))
+
+;; Some helpers, these are used to parse url variables and query
+;; parameters. Their docstrings are used in the API documentation
+
+(defun int (string)
+ "An Integer"
+ (parse-integer string))
+
+(defun str (string)
+ "A String"
+ string)
+
+;; In the following, two query parameters are specififed. NAME is
+;; meant to be a string and AGE is an integer. If AGE is not an integer,
+;; a 500 error will be returned. The syntax is (VAR PARSER)
+(defendpoint* :get "/search" ((name str) (age int)) ()
+ "Echo the search parameters in a nice list."
+ (http-ok (format nil "Name: ~a~%age: ~a~%" name age)))
+
+(defun crapshoot-authorizer ()
+ "Randomly decides that the request is authorized"
+ (< 5 (random 10)))
+
+(defendpoint* :post "/crapshoot" ()
+ (:auth 'crapshoot-authorizer) ; use a custom authorizer
+ "Echos back 'You made it' if the request was authorized"
+ (http-ok "You made it"))
+
+;; Route variables can accept parsers / preformatters
+;; these will parse a value and supply it to the argument of the handler.
+(defendpoint* :get "/random/:lo int:/:hi int:" () ()
+ "Echo back a random number between lo and hi"
+ (if (< lo hi)
+ (http-ok
+ (format nil "The number is: ~a~%"
+ (+ lo (random (- hi lo)))))
+ (http-err 404))) ; Can't find a number X such that LO >= HI and LO < X < HI
+
+(defun person-by-id (id)
+ "ID of a person"
+ ;; The real thing might perform some database operation here. If the
+ ;; operation failed, an error could be signalled, in which case a
+ ;; 500 response would be sent to the client.
+ (list :name "Colin" :occupation "Macrologist" :id (parse-integer id)))
+
+
+(defendpoint* :get "/person/:person person-by-id:" ()
+ (:content-type "application/json") ; override the app's default content type for HTTP responses
+ "Returns a json representation of the person."
+ (http-ok
+ (jonathan:to-json person)))
+