Welcome to the first blog post of 2021! (funny enough also my first blog post)
Recently while trying to work on a direct integration between a React Single Page Application and the GraphQL endpoint of the microservice, I encountered the following errors in the React application:
It looked like the React application (although located on the same server) was not able to establish a connection to the content service GraphQL endpoint.
Upon further inspection, it was obvious that CORS was enabled for the /cd/api endpoint and cross-domain usage was not allowed (docs.sdl.com).
Now there are two ways to solve this problem:
- Using a middleware like Nginx to proxy the content service and pass these headers through
- Enable sending the CORS headers via the Spring Boot application (content service in our case)
I choose option 2 to get this to work.
We don’t really have the source code, do we?
Well, that’s where Dependency Injection comes into play and the Tridion microservices are built using Spring Boot.
I’m working with Tridion 9.5 here which uses Java 11 with Spring Boot 2.1.14.RELEASE. You can easily find the right version of Spring Boot by looking into the /lib folder of the microservices.
And the way Spring Boot works in microservices is by parsing all items under a package-name. As long as the following three conditions are met, your custom Filter will be automatically loaded:
- Filter is located under package com.sdl.delivery.content.interceptor;
- Filter class is annotated with @Component
- Filter class has the highest order @Order(Ordered.HIGHEST_PRECEDENCE)
The solution was to write a custom HTTP Filter, wrap it into a jar file and drop it into the lib folder of the content service*.
The filter adds the following headers to every HTTP request:
response.setHeader("Access-control-Allow-Origin", "*"); response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE"); response.setHeader("Access-Control-Allow-Headers", "x-requested-with, x-auth-token"); response.setHeader("Access-Control-Max-Age", "3600"); response.setHeader("Access-Control-Allow-Credentials", "true");
If it’s a pre-flight CORS request then the following are added along with the previous ones:
response.setHeader("Access-Control-Allowed-Methods", "POST, GET, DELETE"); response.setHeader("Access-Control-Max-Age", "3600"); response.setHeader("Access-Control-Allow-Headers", "authorization, content-type,x-auth-token, " + "access-control-request-headers, access-control-request-method, accept, origin, authorization, x-requested-with");
After doing the stuff above, the requests from the react application were successful and I was actually able to get some data back from the content service:
The maven project for the filter can be found here: NoCorsFilter.java
A pre-built version of the jar file can be downloaded here: nocors-filter-1.0.jar
*Disclaimer: using the lib folder is not a best practice, the right approach is to create a custom folder under the services folder then reinstall the microservice to register the newly created folder