joaotavora / snooze

Common Lisp RESTful web development
207 stars 23 forks source link

OPTIONS method verb missing for CORS pre-flight request. #14

Open skidd0 opened 5 years ago

skidd0 commented 5 years ago

The Issue

This issue is encountered when the server receives an OPTIONS request during a CORS pre-flight. When a browser is about to make a cross domain request that isn't a "basic" request (in this case, a non-basic POST request with json data), it first sends an OPTIONS request to the server for verification that the server supports the actual request. For more, see this link[1].

With snooze, attempting to create a route that handles the OPTIONS method results in an error:

;; the regular route I want to post data to
(defroute create-list (:post "application/json")  
  (do-some-stuff))   

;; route to handle the options pre-flight request
(defroute create-list (:options :text/*))  

On trying to load the code

; in: DEFROUTE CREATE-LIST
;     (SNOOZE:DEFROUTE QU-DU.SERVER::CREATE-LIST
;       (:OPTIONS))
; 
; caught ERROR:
;   (during macroexpansion of (DEFROUTE CREATE-LIST
;     ...))
;   Sorry, don't know the HTTP verb OPTIONS

I have set up hunchentoot to add the appropriate headers in responses:

(defmethod hunchentoot:acceptor-dispatch-request :around ((a snooze-acceptor) request)
  (let ((hunchentoot:*dispatch-table* *server-dispatch-table*))
    (setf (hunchentoot:header-out "Accept") "*/*")
    (setf (hunchentoot:header-out "Access-Control-Allow-Headers") "Content-Type, Accept, Origin") 
    (setf (hunchentoot:header-out "Access-Control-Allow-Methods") "POST, GET, OPTIONS, PUT, DELETE") 
    (setf (hunchentoot:header-out "Access-Control-Allow-Origin") "*") 
    (call-next-method)))

My Workaround So Far

In snoozes common.lisp source file, I have made a small addition:

(cl:defclass snooze-verbs:http-verb      () ())
(cl:defclass snooze-verbs:delete         (snooze-verbs:http-verb) ())
(cl:defclass snooze-verbs:content-verb   (snooze-verbs:http-verb) ())
(cl:defclass snooze-verbs:receiving-verb (snooze-verbs:content-verb) ())
(cl:defclass snooze-verbs:sending-verb   (snooze-verbs:content-verb) ())
(cl:defclass snooze-verbs:options        (snooze-verbs:sending-verb) ())  ;; my addition
(cl:defclass snooze-verbs:post           (snooze-verbs:receiving-verb) ())
(cl:defclass snooze-verbs:put            (snooze-verbs:receiving-verb) ())
(cl:defclass snooze-verbs:get            (snooze-verbs:sending-verb) ())

Now the code compiles and loads. When trying to post json to my route, the pre-flight OPTIONS request sent by the browser receives a 204 response as seen below. I still run into a 400 Bad Request error, but I think that's an issue with my code, not with the snooze library.

There may be a better way to go about adding OPTIONS support. I tried creating the snooze-verb:options class as a subclass of http-verb, and content-verb but both had some issues.

image

[1] https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS