Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hunchentoot dispatch by HTTP method

I couldn't find any documentation on how to dispatch based on HTTP method (on the same uri). The closest I got was :default-request-type on the define-easy-handler -- but it seems to dispatch to the latter, even though I use GET method:

(define-easy-handler (index :uri "/" :default-request-type :get) ()
  (log-message* :info "GET on index ------ ")
  (format nil "Hello World"))

(define-easy-handler (echo :uri "/" :default-request-type :post) ()
  (log-message* :info "POST on index ------ ")
  (format nil "~S" (raw-post-data :force-text t)))
like image 540
mck Avatar asked Sep 27 '13 23:09

mck


2 Answers

The (perhaps slightly deceptively named) :uri parameter is allowed to be either a string or a predicate on the request object. So, you can pass a function there that checks if the method and path match. I wrote a macro to make it prettier:

(defmacro method-path (methods path)
  "Expands to a predicate the returns true of the Hunchtoot request
has a SCRIPT-NAME matching the PATH and METHOD in the list of METHODS.
You may pass a single method as a designator for the list containing
only that method."
  (declare
   (type (or keyword list) methods)
   (type string path))
  `(lambda (request)
     (and (member (hunchentoot:request-method* request)
                 ,(if (keywordp methods)
                      `'(,methods)
                      `',methods))
          (string= (hunchentoot:script-name* request)
                   ,path))))

(hunchentoot:define-easy-handler (get-handler :uri (method-path :get "/hello")) ()
  "hello!")

(hunchentoot:define-easy-handler (post-handler :uri (method-path (:post :put) "/hello")) ()
  "a post or a put!")

In the case where the path is found but the method isn't, we should probably return an HTTP 405 error instead of the 404 error that Hunchentoot returns when no handlers match. In order to do this, you could manually write a catch-all handler for every path you define. The 405 response is supposed to include a list of acceptable methods, and I can't think of an easy way to generate one short of modifying define-easy-handler to support specialization on method directly, which might be a good idea.

like image 74
Samuel Edwin Ward Avatar answered Sep 16 '22 18:09

Samuel Edwin Ward


Many frameworks built on top of hunchentoot have that. Restas and Caveman are just two examples. For example in Restas you can say:

(restas:define-route foo ("/foo" :method :get)
  ; some code here
  )

(restas:define-route foo/post ("/foo" :method :post)
  ; some other code here
  )
like image 29
Pavel Penev Avatar answered Sep 17 '22 18:09

Pavel Penev