-
Notifications
You must be signed in to change notification settings - Fork 3
CORS
Cross-Origin Resource Sharing (CORS) is a standard that allows scripts running in a web page to make requests to origins that are different from their own.
For example, a script in a page served from www.example.com would not normally be allowed to make an HTTP request to api.example.com. The browser security model and the same-origin policy forbids it.
CORS defines a set of headers that the server can return to remove this restriction. In our example, the server at api.example.com can return CORS headers to say that it allows requests from www.example.com. When it receives these headers the browser will allow the script to make the request.
This document assumes a basic understanding of CORS and the different types of CORS requests (simple and preflighted). Please see the Mozilla documentation for a full explanation of CORS.
Osiris has built-in support for generating CORS headers and responding to preflight OPTIONS requests. It provides a way to define the CORS headers in a single place so they are added to the response for every CORS-enabled endpoint in the API.
The built-in CORS support is useful for APIs where a large number of endpoints must support CORS and especially for APIs that must support preflighted requests.
In simple cases where there are not many CORS endpoints and they do not need preflight requests it is also an option to add the CORS headers to the response manually instead of using the built-in CORS support.
CORS support in Osiris is enabled by doing two things:
- Setting the
corsflag on the API or endpoint - Defining a
corsblock in the API definition
The cors flag is a boolean that indicates an endpoint should be CORS-enabled. The flag can be set on the API, in which case all endpoints are CORS-enabled, or it can be set on individual endpoints.
// CORS is enabled by default for all endpoints in the API
val api = api<FooComponents>(cors = true) {
// This endpoint is CORS-enabled
get("/foo") {
}
// This endpoint is not CORS-enabled
get("/bar", cors = false) {
}
}// CORS is not enabled by default for endpoints in the API
val api = api<FooComponents> {
// This endpoint is CORS-enabled
get("/foo", cors = true) {
}
// This endpoint is not CORS-enabled
get("/bar") {
}
}Setting the cors flag for an endpoint has two effects:
-
OPTIONSrequests are enabled for the path to support preflighted requests - The CORS headers defined in the
corsblock are added to the response (see below)
The cors block in an API definition specifies the CORS headers that should be added to the response for all CORS-enabled endpoints. There are three properties that can be set in the cors block, corresponding to the standard CORS headers. These are allowOrigin, allowMethods and allowHeaders. For example:
val api = api<FooComponents>(cors = true) {
cors { req ->
allowMethods = setOf(HttpMethod.POST)
allowOrigin = setOf("*")
allowHeaders = setOf(HttpHeaders.CONTENT_TYPE)
}
get("/foo") {
}
}These headers are added to the responses for all requests to a CORS-enabled endpoint. This includes simple CORS requests and also preflight OPTIONS requests. It is not necessary to manually define options handlers to handle preflight requests. Osiris takes care of this.
The request is passed to the cors block (the req parameter in the example above). This allows different headers to be generated for different endpoints. In most cases (including the example above) this isn't necessary, and the same headers are returned for all endpoints. In that case the req parameter can be omitted.
In simple cases where there are a small number of CORS endpoints and the requests are all simple then it can be simpler to define the CORS headers in the handler instead of using a cors block. For example:
val api = api<FooComponents> {
// The cors flag is not set for the endpoint so Osiris doesn't handle CORS
get("/foo") { req ->
req.responseBuilder()
// Add a CORS header to allow requests from www.example.com
.header("Access-Control-Allow-Origin", "www.example.com")
.build("hello, world!")
}
}It is possible to handle preflighted requests in the same way, but in that case you must define an options handler for every path that must handle preflight CORS requests.