Beginning with newly-released Helidon 2.0 [1], you can easily add CORS support to your application and to Helidon’s built-in services for health, metrics, and OpenAPI.
A same-origin policy [2], implemented in browsers and other well-behaved web clients, restricts a page or app from one origin — URL scheme, host, and port — from accessing resources from other origins.
But, in certain cases, relaxing that restriction makes sense. Cross-origin resource sharing (CORS) [3] is a standard approach using HTTP headers in which web clients ask for and servers grant (or refuse) controlled access to resources from one origin on behalf of a resource in a different origin.
With the recent release of Helidon 2.0, you can easily add CORS support to the endpoints in your own SE [4] and MP [5] applications as well as the endpoints of built-in Helidon services such as health, metrics, and OpenAPI.
The examples below are inspired by the Helidon greeting example app [6, 7]. We’ll assume that you want to permit resources from http://foo.com
and http://there.com
to have PUT
and DELETE
access to your app’s resources while granting simple access — GET
, HEAD
, etc. — to all other origins.
CORS in Helidon SE
Add this dependency to your pom.xml
<dependency>
<groupId>io.helidon.webserver</groupId>
<artifactId>helidon-webserver-cors</artifactId>
</dependency>
Create a CorsSupport
instance in your code
Build a CorsSupport
object to represent the CORS behavior you want for your app’s resources. You use two CrossOriginConfig
objects, one for the PUT
and DELETE
access and a second for all other access:
CorsSupport corsSupport = CorsSupport.builder()
.addCrossOriginConfig(CrossOriginConfig.builder()
.allowOrigins("http://foo.com", "http://there.com")
.allowMethods("PUT", "DELETE")
.build())
.addCrossOriginConfig(CrossOriginConfig.create())
.build();
Use the CorsSupport instance
Use the instance of CorsSupport
in the same register
call that sets up routing for your application’s path:
return Routing.builder()
.register(JsonSupport.create())
.register(health) // Health at "/health"
.register(metrics) // Metrics at "/metrics"
.register("/greet", corsSupport, greetService)
.build();
Then rebuild and restart your app. That’s it. See below for some simple ways to see the CORS support in action with your app.
CORS in Helidon MP
Add this dependency to your pom.xml
<dependency>
<groupId>io.helidon.microprofile</groupId>
<artifactId>helidon-microprofile-cors</artifactId>
</dependency>
Use the @CrossOrigin
annotation
In your JAX-RS resource class, add a no-op method for HTTP OPTIONS
requests and annotate it:
@OPTIONS
@CrossOrigin(
allowMethods = {"PUT", "DELETE"},
allowOrigins = {"http://foo.com", "http://there.com"})
public void optionsForGreeting() {}
Then rebuild and restart your app.
Trying it out (SE and MP)
You can use curl
to see Helidon’s CORS support in action for your app’s endpoints. The code snippets below highlight the CORS-related headers.
For “simple” operations — GET
, HEAD
, etc. — CORS simply adds headers to the normal HTTP request.
curl -i -X GET \
-H "Origin: http://foo.com" -H "Host: here.com" \
http://localhost:8080/greet
And to the response.
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Content-Type: application/json
Date: Thu, 30 Apr 2020 17:25:51 -0500
Vary: Origin
connection: keep-alive
content-length: 27
{"greeting":"Hello World!"}
For so-called “non-simple” operations that can change the application’s server-side state, CORS uses a pre-flight request that lets the server “pre-approve” the desired access as declared by the client. Usually a browser sends pre-flight requests (and checks the responses) invisibly, but the followingcurl
command simulates this step:
curl -i -X OPTIONS \
-H "Access-Control-Request-Method: PUT" \
-H "Origin: http://foo.com" \
-H "Host: here.com" \
http://localhost:8080/greet/greeting
The server indicates that it is OK with the requested cross-origin resource sharing:
HTTP/1.1 200 OK
Access-Control-Allow-Methods: PUT
Access-Control-Allow-Origin: http://foo.com
Date: Thu, 30 Apr 2020 17:30:59 -0500
transfer-encoding: chunked
connection: keep-alive
At this point the client issues the desired PUT
request, in our example to change the greeting:
curl -i -X PUT \
-H "Origin: http://foo.com" \
-H "Host: here.com" \
-H "Access-Control-Allow-Methods: PUT" \
-H "Access-Control-Allow-Origin: http://foo.com" \
-H "Content-Type: application/json" \
-d "{ \"greeting\" : \"Cheers\" }" \
http://localhost:8080/greet/greeting
The server responds normally:
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: http://foo.com
Date: Thu, 30 Apr 2020 17:32:55 -0500
Vary: Origin
connection: keep-alive
Bottom Line
With very little extra code, your Helidon SE or MP application’s endpoints can participate in the CORS protocol. And we’ve only hit the high points here. You can use configuration to simplify your CORS-related changes even more.
Further, you can easily turn on and control CORS for Helidon’s built-in services that add endpoints to your application: health, metrics, and OpenAPI.
For complete information, please refer to the documentation ([4], [5]).
References
[1] https://helidon.io
[2] https://www.w3.org/Security/wiki/Same_Origin_Policy
[3] https://www.w3.org/TR/cors/
[4] https://helidon.io/docs/latest/#/se/cors/01_introduction
[5] https://helidon.io/docs/latest/#/mp/cors/01_introduction
[6] https://github.com/oracle/helidon/tree/latest/examples/cors
[7] https://github.com/oracle/helidon/tree/latest/examples/microprofile/cors