-3

I am trying to build out an Angular & Spring Boot app with Webflux, and instead of starting by getting bogged down by a full CRUD tutorial with entities and databases I thought I'd start by just seeing if I could get the client to display a string returned by the server, as spring boot's documentation teaches here. To that end, I have the following code:

//Angular client
  private currencyGreetingUrl: string;

  constructor(private http: HttpClient) {
    this.currencyGreetingUrl = 'http://localhost:8080/currency';
  }

  public getCurrencyGreeting(): Observable<string> {
    return this.http.get<string>(this.currencyGreetingUrl);
  }

//Spring Boot Web Client
@CrossOrigin(origins = "http://localhost:4200")
public class CurrencyWebClient {
  private WebClient client = WebClient.create("http://localhost:8080");

  private Mono<ClientResponse> result = client.get()
      .uri("/currency")
      .accept(MediaType.TEXT_PLAIN)
      .exchange();
  
  public String getResult() {
    return ">> result = " + result.flatMap(res -> res.bodyToMono(String.class)).block();
  }
}

//Spring boot handler
@Component
public class CurrencyHandler {

  public Mono<ServerResponse> currency(ServerRequest request) {
    return ServerResponse.ok().contentType(MediaType.TEXT_PLAIN)
      .body(BodyInserters.fromValue("Hello, Currency On The Go!"));
  }
}

//Spring Boot router

@Configuration
public class CurrencyRouter {

  @Bean
  public RouterFunction<ServerResponse> route(CurrencyHandler currencyHandler) {

    return RouterFunctions
      .route(RequestPredicates.GET("/currency").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), currencyHandler::currency);
  }
}

However, I get this error when running the client and server Access to XMLHttpRequest at 'http://localhost:8080/currency' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

I am new to Spring Boot and have been looking around for advice, but I've generally found solutions that involve configurations, controller classes, and other more built out artifacts than what I currently have. How could I get this to work? I appreciate any help.

eko
  • 39,722
  • 10
  • 72
  • 98
amdorsey12
  • 453
  • 5
  • 17
  • 1
    Cors is THE MOST COMMON question here on stack overflow. The spring documentation tells you exactly how to gix that including 100s of tutorials online and 50 questions per week here on stack overflow. – Toerktumlare May 28 '21 at 22:55
  • 1
    it is the most asked question under the java spring-boot tag. And i have read your question, and my answer to you is https://meta.stackoverflow.com/questions/261592/how-much-research-effort-is-expected-of-stack-overflow-users take notice of the 2 downvotes. – Toerktumlare May 29 '21 at 00:09
  • 2
    there is insane amount of documentation on CORS https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux-cors https://spring.io/guides/gs/rest-service-cors/ https://spring.io/blog/2015/06/08/cors-support-in-spring-framework https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#features.developing-web-applications.spring-mvc.cors https://en.wikipedia.org/wiki/Cross-origin_resource_sharing https://fetch.spec.whatwg.org/#http-cors-protocol – Toerktumlare May 29 '21 at 00:16
  • in the future, use google and find the official documentation for the framework you are using and start doing research before asking on stack overflow. Here is your answer https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux-cors-webfilter it is very conveniently located in the official webflux documentation, in the CORS chapter. – Toerktumlare May 29 '21 at 00:20

4 Answers4

3

You have decided to use functional endpoints and you are not using spring security of what i can tell from your tags which means all other answers provided here are faulty.

When using functional endpoints you have to be aware that you have opted out of using the traditional annotation based classes. Which means you loose a lot of the free functionality provided by the spring framework

Functional endpoints are more "low level". So the responsibility falls on you the developer to handle CORS.

If you are a beginner on Spring and webflux i would not pick this route.

The documentation states in the CORS chapter in the official webflux documentation:

You can apply CORS support through the built-in CorsWebFilter, which is a good fit with functional endpoints.

They even provide an implementation of a standard CORS filter that you can customize to your needs.

@Bean
CorsWebFilter corsFilter() {

    CorsConfiguration config = new CorsConfiguration();

    // Possibly...
    // config.applyPermitDefaultValues()

    config.setAllowCredentials(true);
    config.addAllowedOrigin("https://domain1.com");
    config.addAllowedHeader("*");
    config.addAllowedMethod("*");

    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", config);

    return new CorsWebFilter(source);
}

There is 100s of webpages on the internet that explain CORS and it is the most common question asked in the spring-boot, spring-security tag. I suggest you read up on CORS (which you should of done before asking here).

I highly suggest you start reading here:

Mozilla CORS

Toerktumlare
  • 12,548
  • 3
  • 35
  • 54
  • Very well, thanks. The fact of the matter is that I wanted to "get into spring boot" but when I started looking that up the difficulty was that there were just too many options for how to make such an app for me to decide on what to use- gradle v maven, wars v jars, what dependencies should I use, on and on. – amdorsey12 May 29 '21 at 01:13
  • So, I thought it would be easy enough to just connect the [tutorial](https://spring.io/guides/gs/reactive-rest-service/) to my client. It's not like I didn't try to search how to enable cors, you see in my question that I added an annotation to the WebClient. Apparently, my mistake was that I wasn't considering the functional endpoints, which don't work the same as if I used spring security. – amdorsey12 May 29 '21 at 01:22
  • Is the bean supposed to be injected into another class? Read through that baeldung article you got the bean from, clicked into related articles, did searches about functional endpoints and it didn't answer that question. I made a file with the bean a level below the app root, changed the allowed origin to ```http://localhost:4200``` and still got the error. Tried changing the allowed method header to ```Access-Control-Allow-Origin``` and allowed methods to ```GET``` and still no luck. – amdorsey12 May 29 '21 at 02:48
  • Stack overflow is not a forum. It is a q&a site. This question has been answered. – Toerktumlare May 29 '21 at 10:01
  • `Is the bean supposed to be injected into another class? Read through that baeldung article you got the bean from` i never get information from baeldung sites because they usually provide faulty outdated information. If you actually read my answer, you'll se that i have liked to the OFFICIAL webflux documentation, that has the code sample. If you don't understand the answer, then you should start from the basics, and maybe start out by implementing a standard spring boot application, or google on beans and where they should be declared etc. – Toerktumlare May 29 '21 at 17:42
  • I used the code from the official documentation. But there, as is in baeldung, it doesn't really make it clear what exactly you're supposed to do with the filter. Is spring supposed to just pick it up at run time and handle adding the headers to the response on its own? Am I supposed to inject it in the handler? I can just scrap this project and start over with the spring security dependency which appears to have a more straightforward answer, I was just hoping to learn doing it this way for my own knowledge. General information on beans, which I did look up, didn't help much – amdorsey12 May 31 '21 at 20:48
  • https://meta.stackoverflow.com/questions/294774/is-stack-overflow-not-a-forum – Toerktumlare Jun 01 '21 at 06:10
0

Have you configured cors properly? If not try following: https://stackoverflow.com/a/67605427/9295125

Please also read the second answer as the posted answer misses one line.

  • Thanks. Trying to follow along to this, but there's issues since I hadn't included security as a dependency. Using gradle, added ```compile "org.springframework.boot:spring-boot-starter-security"``` to my build file, updated the class paths but it looks like it's just not finding it. I'll probably just scrap and remake the project with security as a dependency, hadn't done much yet anyway – amdorsey12 May 29 '21 at 00:22
  • the user has not specified using spring security, from the question at hand, so this is not an answer, it is linking to a faulty answer. – Toerktumlare May 29 '21 at 00:26
  • Sorry. The used dependencies or Pom are not included in the original question – Daniel Rafael Wosch May 29 '21 at 09:08
0

For my purposes when using Webflux with a browser-based front-end where I need to test quickly, I use a CORS-enabling Chrome extension to test quickly. This is obviously not great for production.

If you are in a pure microservice / backend communication situation, you may just need to go with Spring's intended workflow. In my experience, Spring Webflux has some powerful outcomes but finicky setup. Those configuration classes that you mentioned but are avoiding, or annotations or DSL-based solutions for Kotlin are, unfortunately for now, the intended way. In my experience they do work, and once you have them set up it's a matter of making a quick github-ready template for other microservices (I was building Microservices with Spring and Kotlin at the time).

EDIT: I will also just add that those configurations and classes you're avoiding are worth your time, even a morning of your time spent on those will pave the way for a lot of productivity. Webflux is great (and I've used it both with Kotlin and Java) but there are some unavoidable gotchas and configuration differences when you jump to asynchronous / reactive webflux land. The joy in the config comes when you realize you did things right and it's your DB driver that's freaking out when you can push so many requests :) :) :)

bbuck
  • 129
  • 10
  • 1
    Thanks for the advice. Awkward first steps exploring this, it was a consideration between "ok, I want a framework" but also "ok, let's start small." – amdorsey12 May 29 '21 at 00:25
-1

Indeed I did need to use the CorsWebFilter. However, after reading a bit on Spring Boot and its Dependency Injection system, and looking at different tangentially related cors implementation in Spring Boot projects that didn't use functional endpoints, I found that I had to place the ``CorsWebFilter``` at the root of my application like so:

@SpringBootApplication
public class ServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServerApplication.class, args);
        CurrencyWebClient client = new CurrencyWebClient();

        System.out.println(client.getResult());
    }

    @Bean
    CorsWebFilter corsFilter() {

        CorsConfiguration config = new CorsConfiguration();

        config.setAllowCredentials(true);
        config.addAllowedOrigin("http://localhost:4200");
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);

        return new CorsWebFilter(source);
    }
}

This of course, is not the only way to do it. But it solved this little initial problem I had. Thanks all.

amdorsey12
  • 453
  • 5
  • 17