Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

cors issue when throwing a runtime exception with spring and docker

I' building a web app using spring, angular and docker. I also have for the moment two environments. A dev environment where angular is served using npm start and spring using mvn spring-boot:run. And a test environment where I use digitalocean to host my app and containerize the angular and spring apps using docker.

In this application I have a registration page where user can register and a spring controller to handle registration request. Here is the code for my controller :

@PostMapping("/register")
public SaveUserDto register(@RequestBody @Valid SaveUserDto saveUserDto, WebRequest request) {

        User userToSave = this.modelMapper.map(saveUserDto, User.class);
        User registeredUser = null;
        try {
            registeredUser = this.registrationService.register(userToSave);
        } catch (DataIntegrityViolationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            if(e.getRootCause().toString().contains("uk_pfki8mu6sefesty9h30d4n3yv")) {
                throw new EmailAlreadyExistException();
            }
        }
        try {
            eventPublisher.publishEvent(new OnRegistrationCompleteEvent(registeredUser, request.getLocale(), request.getHeader("Origin")));
        } catch (Exception me) {
            System.out.println(me.getMessage());
        }

        if(registeredUser != null) {
            return this.modelMapper.map(registeredUser, SaveUserDto.class);
        } else {
            return null;
        }
}

Since my angular is served from host:4200 (where host is either localhsot in my dev environment, or 165.22.72.253 in my test environment) and my api is served from host:8761 I have a cors configuration to enable cors request. Here is the configuration :

@Bean
public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOrigin("http://" + clientHost + ":4200");
        config.addAllowedOrigin("http://" + clientHost + ":80");
        config.addAllowedHeader("*");
        config.addAllowedMethod("OPTIONS");
        config.addAllowedMethod("GET");
        config.addAllowedMethod("POST");
        config.addAllowedMethod("PUT");
        config.addAllowedMethod("DELETE");
        source.registerCorsConfiguration("/**", config);
        return new CorsFilter(source);
}

Here clientHost is a parameter with either value is localhost when running in dev environment or 165.22.72.253 for my test environment. For most use case this setting is working but it fails in one particular occasion.

The problem arise when a user try to register whith a existing email adress. Then as you can see from the controller method I throw an EmailAlreadyExistException. In dev env everything is working great that is the request return an error code 500 with a message "Email already exist". But in the test environment I get Access to XMLHttpRequest at 'http://165.22.72.253:8762/register' from origin 'http://165.22.72.253:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource..

If useful here is my docker-compose file :

version: "3.5"

services:

  tomcat-file:
    container_name: tomcat-file-server
    image: tomcat-file-server
    build:
      context: .
      dockerfile: tomcat-file.prod.dockerfile
    volumes:
      - /home/vetter_leo/medias:/usr/local/tomcat/src
    ports:
      - "8205:8205"

networks:
  default:
    external:
      name: helenos-network

At this point I don't understand why it's working in dev env and not in test env. Any help will be greatly appreciated. Thanks in advance.

like image 732
Vetouz Avatar asked Apr 05 '20 14:04

Vetouz


People also ask

How do spring boots handle Cors?

To code to set the CORS configuration globally in main Spring Boot application is given below. Now, you can create a Spring Boot web application that runs on 8080 port and your RESTful web service application that can run on the 9090 port.

What is CrossOrigin annotation in spring boot?

This @CrossOrigin annotation enables cross-origin resource sharing only for this specific method. By default, its allows all origins, all headers, and the HTTP methods specified in the @RequestMapping annotation. Also, a maxAge of 30 minutes is used.

What is Cors spring boot?

In Spring boot we have cors which stands for Cross-Origin Resource Sharing, it basically means that the host which is representing the UI, and the host which serves the data both are different. Means both are running on a different host, in such type of cases we may encounter this problem which running the application.


2 Answers

What I finaly ended up to do was adding an handler to the exception in the controller that return me the right message : this way :

@ExceptionHandler({ EmailAlreadyExistException.class })
public String handleException() {
    return "{\"error\": \"" + messages.getMessage("emailAlreadyExist", null, Locale.ENGLISH) + "\"}";
}
like image 135
Vetouz Avatar answered Oct 20 '22 04:10

Vetouz


Did you try to proxy the API calls? In the angular project, you can create a proxy.conf.json file and add below configurations.

{
  "/api": {
    "target": "http://localhost:3000",
    "secure": false
  }
}

Then in the package.json change the start,

"start": "ng serve --proxy-config proxy.conf.json"

If you want to do it from the psring side add @CrossOrigin to controller.

But if you think you are going to face the same issue in production, my advice is to use a reverse proxy like Nginx. You're Angular application and APIs are served via the same port but all the APIs are prefixed with api. From the Nginx side, you can filter API calls using api prefix and proxy them to backend service.

like image 26
Keaz Avatar answered Oct 20 '22 03:10

Keaz