bs-cors
bs-cors is a Buckelscript binding to cors, made by Troy Goode, an expressjs middleware that can be used to enable Cross-origin resource sharing with various options.
Installation
This is a Node.js module available through the
npm registry. Installation is done using the
npm install
command:
$ npm install bs-cors
then add it as a dependency to your bs-config.json :
...
"bs-dependencies": ["bs-express", "bs-cors", ...],
...
Usage
Simple Usage (Enable All CORS Requests)
open Express;let app = express(); App.use(app,Cors.cors()); Middleware.from((_next, _req) => { [("msg", Js.Json.string("This is CORS-enabled for only example.com !"))] |> Js.Dict.fromList |> Js.Json.object_ |> Response.sendJson})|> App.get(app, ~path="/products/:id"); let onListen = e => switch (e) { | exception (Js.Exn.Error(e)) => ( switch (Js.Exn.message(e)) { | None => "UNKNOWN ERROR" | Some(msg) => msg } ) |> Js.log; Node.Process.exit(1); | _ => "CORS-enabled web server listening on port 8080" |> Js.log }; let server = App.listen(app, ~port=8080, ~onListen, ());
Enable CORS for a Single Route
open Express;let app = express(); [| Cors.cors(), Middleware.from((_next, _req) => { [("msg", Js.Json.string("This is CORS-enabled for only example.com !"))] |> Js.Dict.fromList |> Js.Json.object_ |> Response.sendJson }),|]|> App.getWithMany(app, ~path="/products/:id"); let onListen = e => switch (e) { | exception (Js.Exn.Error(e)) => ( switch (Js.Exn.message(e)) { | None => "UNKNOWN ERROR" | Some(msg) => msg } ) |> Js.log; Node.Process.exit(1); | _ => "CORS-enabled web server listening on port 8080" |> Js.log }; let server = App.listen(app, ~port=8080, ~onListen, ());
Configuring CORS
open Express;let app = express(); [| Cors.cors( ~origin=Cors.String("example.com"), ~optionsSuccessStatus=Response.StatusCode.Accepted, (), ), // some legacy browsers (IE11, various SmartTVs) choke on 204 Middleware.from((_next, _req) => { [("msg", Js.Json.string("This is CORS-enabled for only example.com !"))] |> Js.Dict.fromList |> Js.Json.object_ |> Response.sendJson }),|]|> App.getWithMany(app, ~path="/products/:id"); let onListen = e => switch (e) { | exception (Js.Exn.Error(e)) => ( switch (Js.Exn.message(e)) { | None => "UNKNOWN ERROR" | Some(msg) => msg } ) |> Js.log; Node.Process.exit(1); | _ => "CORS-enabled web server listening on port 8080" |> Js.log }; let server = App.listen(app, ~port=8080, ~onListen, ());
Configuring CORS w/ Dynamic Origin
open Express;let app = express(); let whitelist = [|"http:\/\/example1.com", "http:\/\/example2.com"|];let originFunction: (option(string), (option(Js.Exn.t), bool) => unit) => unit = (origin, callback) => switch (origin) { /* If you do not want to block REST tools or server-to-server requests */ | None => callback(None, true) | Some(o) => if (Array.exists(item => item === o, whitelist)) { callback(None, true); } else { callback(Js.Exn.raiseError("Not allowed by CORS"), false); } }; [| Cors.cors(~origin=Function(originFunction), ()), Middleware.from((_next, _req) => { [("msg", Js.Json.string("This is CORS-enabled for only example.com !"))] |> Js.Dict.fromList |> Js.Json.object_ |> Response.sendJson }),|]|> App.getWithMany(app, ~path="/products/:id"); let onListen = e => switch (e) { | exception (Js.Exn.Error(e)) => ( switch (Js.Exn.message(e)) { | None => "UNKNOWN ERROR" | Some(msg) => msg } ) |> Js.log; Node.Process.exit(1); | _ => "CORS-enabled web server listening on port 8080" |> Js.log }; let server = App.listen(app, ~port=8080, ~onListen, ());
Enabling CORS Pre-Flight
Certain CORS requests are considered 'complex' and require an initial
OPTIONS
request (called the "pre-flight request"). An example of a
'complex' CORS request is one that uses an HTTP verb other than
GET/HEAD/POST (such as DELETE) or that uses custom headers. To enable
pre-flighting, you must add a new OPTIONS handler for the route you want
to support:
open Express;let app = express(); App.options(app, ~path="/products/:id", Cors.cors());[| Cors.cors(), Middleware.from((_next, _req) => { [("msg", Js.Json.string("This is CORS-enabled for all origins!"))] |> Js.Dict.fromList |> Js.Json.object_ |> Response.sendJson }),|]|> App.deleteWithMany(app, ~path="/products/:id"); let onListen = e => switch (e) { | exception (Js.Exn.Error(e)) => ( switch (Js.Exn.message(e)) { | None => "UNKNOWN ERROR" | Some(msg) => msg } ) |> Js.log; Node.Process.exit(1); | _ => "CORS-enabled web server listening on port 8080" |> Js.log };
You can also enable pre-flight across-the-board like so:
/* include before other routes */App.options(app, ~path="*", Cors.cors());
Configuring CORS Asynchronously
TODO
Configuration Options
The type of the cors
middleware function is :
let cors: ( ~origin: origin=?, ~methods: array(Express.Request.httpMethod)=?, ~allowedHeaders: option(array(string))=?, ~exposedHeaders: option(array(string))=?, ~credentials: bool=?, ~maxAge: option(int)=?, ~preflightContinue: bool=?, ~optionsSuccessStatus: Express.Response.StatusCode.t=?, unit ) => Express.Middleware.t;
origin
: Configures the Access-Control-Allow-Origin CORS header. It is a variant with thoses constructors:Boolean(bool)
- setorigin
toBoolean(true)
to reflect the request origin, as defined byreq.header('Origin')
, or set it toBoolean(false)
to disable CORS.String(string)
- setorigin
to a specific origin. For example if you set it toString("http:\/\/example.com")
only requests from "http://example.com" will be allowed.RegExp(Js.Re.t)
- setorigin
to a regular expression pattern which will be used to test the request origin. If it's a match, the request origin will be reflected. For example the patternRegExp([%re "/example\.com$/"])
will reflect any request that is coming from an origin ending with "example.com".Array(array(string))
- setorigin
to an array of valid origins using strings.Array(array(Js.Re.t))
- setorigin
to an array of valid origins using RegExp.Function((option(string), (option(Js.Exn.t), bool) => unit) => unit)
- setorigin
to a function implementing some custom logic. The function takes the request origin as the first parameter and a callback
methods
: Configures the Access-Control-Allow-Methods CORS header. Expects an array (ex:Express.Request.([|Get, Put, Post|])
).allowedHeaders
: Configures the Access-Control-Allow-Headers CORS header. Expects an optional array (ex:Some([|"Content-Type", "Authorization"|])
). If not specified, defaults to reflecting the headers specified in the request's Access-Control-Request-Headers header.exposedHeaders
: Configures the Access-Control-Expose-Headers CORS header. Expects an optional array (ex:Some(["Content-Range", "X-Content-Range"])
). If not specified, no custom headers are exposed.credentials
: Configures the Access-Control-Allow-Credentials CORS header. Set totrue
to pass the header, orfalse
to be omitted.maxAge
: Configures the Access-Control-Max-Age CORS header. Set to an optional integer to pass the header, otherwise it is omitted.preflightContinue
: Pass the CORS preflight response to the next handler.optionsSuccessStatus
: Provides a status code to use for successfulOPTIONS
requests, since some legacy browsers (IE11, various SmartTVs) choke on204
.
The default configuration is the equivalent of:
cors( ~origin=String("*"), ~methods=[| Request.Get, Request.Head, Request.Put, Request.Patch, Request.Post, Request.Delete, |], ~allowedHeaders=None, ~exposedHeaders=None, ~credentials=false, ~maxAge=None, ~preflightContinue=false, ~optionsSuccessStatus=Response.StatusCode.NoContent, ());
License
Author
Thanks to
Troy Goode for building cors !!!