diff options
Diffstat (limited to 'example/lazybones-example.lisp')
-rw-r--r-- | example/lazybones-example.lisp | 129 |
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))) + |