Spring Boot and CORS

Spring Boot has had support for enabling CORS in REST controllers since version 1.3.  The @CrossOrigin annotation is straightforward to apply at the method or type level to enable cross origin requests:

@SpringBootApplication
@RestController
@CrossOrigin(origins = "http://localhost:8080")
public class App {
    @PostMapping(path = "/foo", consumes = MediaType.APPLICATION_JSON_VALUE)
    public String postFoo(@RequestBody String body) {
        return "Value posted: " + body;
    }

    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
}

Testing with cURL

cURL is an obvious choice for testing CORS with Spring Boot RESTful API endpoints.

$ curl -v -X POST \
-H "Origin: http://localhost:8080" \
-H "Content-Type: application/json" \
-d "{}" \
http://localhost:8080/foo

results in the following output:

* Hostname was NOT found in DNS cache
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> POST /foo HTTP/1.1
> User-Agent: curl/7.35.0
> Host: localhost:8080
> Accept: */*
> Origin: http://localhost:8080
> Content-Type: application/json
> Content-Length: 2
>
* upload completely sent off: 2 out of 2 bytes
< HTTP/1.1 200
< Content-Type: text/plain;charset=UTF-8
< Content-Length: 16
< Date: Thu, 08 Jun 2017 21:23:55 GMT
<
* Connection #0 to host localhost left intact
Value posted: {}

Changing the value of the Origin header results in the expected HTTP 403 response. Here’s how to test out the CORS preflight OPTIONS request:

$ curl -v -X OPTIONS \
-H "Access-Control-Request-Method: POST" \
-H "Origin: http://localhost:8080" \
-H "Access-Control-Request-Headers: Content-Type" \
http://localhost:8080/foo

results in the following output:

* Hostname was NOT found in DNS cache
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> OPTIONS /foo HTTP/1.1
> User-Agent: curl/7.35.0
> Host: localhost:8080
> Accept: */*
> Access-Control-Request-Method: POST
> Origin: http://localhost:8080
> Access-Control-Request-Headers: Content-Type
>
< HTTP/1.1 200
< Allow: GET, HEAD, POST, PUT, DELETE, OPTIONS, PATCH
< Content-Length: 0
< Date: Wed, 07 Jun 2017 21:20:00 GMT
<
* Connection #0 to host localhost left intact 

Testing with Postman

Postman can be a little tricky if you’re using the Chrome extension. This version automatically adds the Origin header populated with a value like chrome-extension://fhbjgbiflinjbdggehcddcbncdddomop, which can result in a 403 Invalid CORS Request because it doesn’t match the allowed origins. In this case, the Postman native app is a better choice, because it allows you to edit the Origin header.

References

1. Spring REST service with CORS example
2. Postman Origin header
3. Understanding CORS
4. CORS support in Spring