1

I have a Spring Boot Rest application where I need to allow CORS requests.

Requests use the common path <host>:<port>/api/... and so far I only have two resources:

package my.controllers;

@RestController
@RequestMapping(path="/", produces="application/json")
public class DataController {

    @GetMapping("/")
    public ApiResponse<Map<String, Object>> getValues() {
        // ...
    }

    @GetMapping("/sub")
    public ApiResponse<String> getValuesSub() {
        // ...
    }

Here are two example requests from the Javascript client running at http://localhost:3000/:

fetch('http://localhost:2001/api/').then(/* ... */);
fetch('http://localhost:2001/api/sub')).then(/* ... */);

If I add the @CrossOrigin(origins = "*") annotation to the controller, CORS requests work fine; if I replace it implementing WebMvcConfigurer.addCorsMappings() though:

@ComponentScan(basePackages={ "my.controllers" })
@Configuration
public class WebappConfig implements WebMvcConfigurer {

    // ...

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        LogManager.getLogger(getClass()).info("CORS settings");
        registry.addMapping("/api/**").allowedOrigins("*").maxAge(3600);
    }

CORS requests fail, and I get (in Firefox, Chrome shows a similar error message):

CORS header 'Access-Control-Allow-Origin' missing

I see the log entry, so the method is surely invoked.

I've seen other questions mentioning Spring Security or Spring Data (I use none), so here's my dependencies list in case I'm missing something:

  • spring-boot-starter-web
  • spring-boot-starter-tomcat
  • spring-boot-starter-log4j2
  • spring-boot-configuration-processor
  • commons-lang3
  • commons-beanutils

What's the right way to set CORS settings to the whole application, without using the @CrossOrigin annotation on each controller?

UPDATE

I'm initializing a Rest servlet like this:

@Bean
public ServletRegistrationBean<DispatcherServlet> registerDispatcherServlet(DispatcherServlet servlet) {
    ServletRegistrationBean<DispatcherServlet> registration = new ServletRegistrationBean<>(servlet);

    servlet.setContextConfigLocation("");
    registration.addUrlMappings("/api/*");
    registration.setLoadOnStartup(1);

    return registration;
}

The logs says:

Servlet dispatcherServlet mapped to [/api/*]
Servlet dispatcherServlet mapped to [/]
Servlet dispatcherServlet was not registered (possibly already registered?)

Could it be that the given Cors settings are going to the mentioned unregistered servlet?

watery
  • 5,026
  • 9
  • 52
  • 92
  • you can add a Cors filter https://stackoverflow.com/a/40418894/379906 – db80 Mar 31 '20 at 09:01
  • @db80 Thank you, I'll keep that as a secondary option, but first I'd like to understand why my configuration is not working. – watery Mar 31 '20 at 09:13

1 Answers1

0

I got it working by replacing "/api/**" with "/**" in the call to addMapping() when configuring the CORS registry:

@Override
public void addCorsMappings(CorsRegistry registry) {
    LogManager.getLogger(getClass()).info("CORS things!");
    registry.addMapping("/**").allowedOrigins("*").maxAge(3600);
}

Though I'm puzzled about why this is the setting that makes it work, and what would I have to do if I need to expose several independent Rest paths, i.e.:

  • http:/host:port/public-api (from all over the Internet)
  • http:/host:port/reserved-api (from well known hosts)
  • http:/host:port/admin-api (same host only)
watery
  • 5,026
  • 9
  • 52
  • 92