CORS Headers for a Spring Boot Kotlin WebFlux Reactor project

In one of our projects we use Spring Boot in combination with the WebFlux starter and the Reactor Core. The WebFilter in the code block below enables CORS for all requests and any remote origin.

If enabled, CORS (Cross-origin resource sharing) allows a web page to use resources from another domain which are usually restricted. When you develop a web application on your local machine you often need CORS even if the production web page does not.

The CORS enabling code samples in this Blog post are written in Kotlin. Since May 2017 Kotlin is an official Android language.  The JVM–based language compiles into Java byte code and is interoperable with Java libraries. I can only recommend to give it a try and enjoy leaving auto-generated boilerplate behind. The language reminds me of Groovy and Scala though it is more than a mix of those two.

Spring Boot is an application we use to build Java web–applications. The following CORS Component is a WebFilter thus executed for any HTTP request before going to the path–specific code. The @Component annotation makes the WebFilter visible to Spring Boot so it is wired–up during application startup.
The WebFlux starter for Spring Boot leverages  non-blocking request processing and provides a functional API which works smoothly with Kotlin. If you want to get deep into Spring Boot WebFlux Kotlin Application check out this full–size project on GitHub.

import org.springframework.http.HttpMethod import org.springframework.http.HttpStatus import org.springframework.stereotype.Component import org.springframework.web.server.ServerWebExchange import org.springframework.web.server.WebFilter import org.springframework.web.server.WebFilterChain import reactor.core.publisher.Mono @Component class CorsFilter : WebFilter { override fun filter(ctx: ServerWebExchange?, chain: WebFilterChain?): Mono<Void> { if (ctx != null) { ctx.response.headers.add("Access-Control-Allow-Origin", "*") ctx.response.headers.add("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE, OPTIONS") ctx.response.headers.add("Access-Control-Allow-Headers", "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range") if (ctx.request.method == HttpMethod.OPTIONS) { ctx.response.headers.add("Access-Control-Max-Age", "1728000") ctx.response.statusCode = HttpStatus.NO_CONTENT return Mono.empty() } else { ctx.response.headers.add("Access-Control-Expose-Headers", "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range") return chain?.filter(ctx) ?: Mono.empty() } } else { return chain?.filter(ctx) ?: Mono.empty() } } }