Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Allow multiple headers with CORS in Suave

Tags:

f#

suave

I'm trying to get my Suave API to accept CORS requests. I've followed this snippet here:

http://www.fssnip.net/mL/title/CORS-response-with-Suave

Which I will recreate here:

let setCORSHeaders =
    setHeader  "Access-Control-Allow-Origin" "*"
    >=> setHeader "Access-Control-Allow-Headers" "content-type"

let allow_cors : WebPart =
    choose [
        OPTIONS >=>
            fun context ->
                context |> (
                    setCORSHeaders
                    >=> OK "CORS approved" )
    ]

let webSite =
    choose [
        allow_cors
        GET >=> OK "URLs are for wimps. GETting something? This is what you get."
    ]

But now I have endpoints that require a token to be passed in, and those endpoints are giving errors due to missing allowed headers in CORS. My token header is simply "token", so I've tried two things, and both have not resolved the issue.

Attempt #1

    setHeader  "Access-Control-Allow-Origin" "*"
    >=> setHeader "Access-Control-Allow-Headers" "content-type"
    >=> setHeader "Access-Control-Allow-Headers" "token"

This returned an error saying content-type was no longer accepted - this seems to imply that the last setHeader overwrote the first one, which when you view the source code here : https://github.com/SuaveIO/suave/blob/master/src/Suave.Tests/HttpWriters.fs , at line 113, there is a test that to me implies this is the desired behavior.

Attempt #2

Based on the response on this question: How to set a Json response in suave webpart, I attempted to set both headers via a comma-separated list:

    setHeader  "Access-Control-Allow-Origin" "*"
    >=> setHeader "Access-Control-Allow-Headers" "Content-Type,token"

But this then gave an error indicating that CORS was failing entirely:

No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:4200' is therefore not allowed access. The response had HTTP status code 401.

Also based on this same question, I tried the following:

    setHeader  "Access-Control-Allow-Origin" "*"
    >=> setHeader "Access-Control-Allow-Headers:content-type" "Accept"
    >=> setHeader "Access-Control-Allow-Headers:token" "Accept"

Which gave this response: Request header field token is not allowed by Access-Control-Allow-Headers in preflight response.

So, how do I allow any amount of headers in a CORS request in Suave?

Edit

Attempt #3

I used addHeader instead of setHeader:

let setCORSHeaders =
    setHeader  "Access-Control-Allow-Origin" "*"
    >=> addHeader "Access-Control-Allow-Headers" "content-type"
    >=> addHeader "Access-Control-Allow-Headers" "token"

Which is now saying... No 'Access-Control-Allow-Origin' header is present on the requested resource.

If I change that first setHeader to addHeader, I still get the same response.

Fix

    addHeader  "Access-Control-Allow-Origin" "*" 
    >=> setHeader "Access-Control-Allow-Headers" "token" 
    >=> addHeader "Access-Control-Allow-Headers" "content-type" 
    >=> addHeader "Access-Control-Allow-Methods" "GET,POST,PUT" 

Did the job - after this, there were issues with what was being sent, but nothing more with CORS itself.

like image 396
Max Avatar asked Jun 04 '17 22:06

Max


2 Answers

Try using addHeader instead of setHeader. Just a few lines further down in the unit tests that you found, there's a test for addHeader that shows that it has the semantics you're looking for, and its documentation says:

Adds the header key with the given value to the list of returned headers, even if that header already exists. This means that Suave will serve a a response with the header denoted by key with possibly different values.

This looks like the behavior you're wanting.

like image 115
rmunn Avatar answered Sep 18 '22 12:09

rmunn


I had a similar problem. Due to legacy reasons I'm stuck for the moment with verion 1.1.3 even though Suave 2.2.1 is available. I'm not really familiar with F# but eventually I managed to get this working with somehting like this:

let setCORSHeaders =
    addHeader  "Access-Control-Allow-Origin" "*" 
    >=> setHeader "Access-Control-Allow-Headers" "token" 
    >=> addHeader "Access-Control-Allow-Headers" "content-type" 
    >=> addHeader "Access-Control-Allow-Methods" "GET,POST,PUT" 

let app =
    choose [
        GET >=>
            fun context ->
                context |> (
                    setCORSHeaders
                    >=> choose
                        [ pathRegex "(.*?)\.(dll|mdb|log)$" >=> dllFilesRequest
                        pathRegex "(.*?)\.(html|css|js|png|jpg|ico|bmp)$" >=> staticFilesRequest

                        path "/" >=> indexRequest
                        path "/index" >=> indexRequest
                        path "/static" >=> staticFilesRequest
                        // ...
                        ] )

        POST >=>
            fun context ->
                context |> (
                    setCORSHeaders
                    >=> choose
                        [
                        path "/something" >=> runSomething
                        // ...
                        ] )
    ]

I'm sure there is a pretier way.

like image 26
Adrian Moisa Avatar answered Sep 21 '22 12:09

Adrian Moisa