3

How to fix CORS error in my React and spring application.

I've implemented a simple method to get list of users. I've tested this endpoint using postman. Now I'm fetching this list from my react js application using fetch API. I've added the @CrossOrigin annotation in my spring backend. I've also added a proxy to my package.json file in my React application.

React JS code :-

import React from 'react';
import { Component } from 'react';

class DropDownUsers extends Component {
  state = {
    users : [],
    selectedUser : "",
    validationError : ""
  }

  componentDidMount() {

   fetch("http://localhost:8080/api/users")
    .then(data => {
      let usersFromApi = data.map(user => {
        return {
          value : user,
          display : user
        }})
      this.setState({ users : [{value: '', display: '(Choose the user)'}].concat(usersFromApi)});
    })
      .then((response) => {
        return response.json();
      })
      .catch(error => {
        console.log(error);
      });
  }

  render() {
    return (
      <div>
        <select value={this.state.selectedUser}
                onChange={(e) => this.setState({selectedUser: e.target.value})}
         >
         {this.state.users.map((user) => <option key={user.value} value={user.value}> {user.display}</option>)}
        </select>
        <div style={{color:'red',marginTop:'5px'}}>
          {this.state.validationError}
        </div>
      </div>
    )
  }
}

export default DropDownUsers;

Error message :-

Access to fetch at 'http://localhost:8080/api/users' from origin 'http://localhost:3001' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled

Spring Config file :-

public ConfigurableServletWebServerFactory servletContainer() {
        final TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();

        if (ajpEnabled) {
            factory.setProtocol("AJP/1.3");

            factory.addConnectorCustomizers(connector -> {
                connector.setSecure(false);
                connector.setScheme("http");
                connector.setAllowTrace(true);
                connector.setPort(8009);
            });
        }

        return factory;
    }

Spring Controller :-

@Slf4j
@EnableLogging
@CrossOrigin(origins = "http://localhost:3001")
@Path("/users")
public class ListOfUsers {

    @Autowired
    DAO DAO;

    @GET
    @Produces(APPLICATION_JSON)
    public Response execute(@Valid ListOfUsersBean listOfUsersBean) {

        final Optional<List<ListOfUsersBean>> user = DAO.getTotalListOfUsers();
        return status(SC_OK).entity(user).build();
    }

    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurerAdapter() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/**").allowedOrigins("*");
            }
        };
    }

}

I want to list the users in a dropdown menu. Any help would be appreciated.

Aravind
  • 256
  • 1
  • 4
  • 17
  • Dod you restart the Spring application? – Matthew Gaiser Oct 03 '19 at 06:26
  • 1
    @MatthewGaiser I did restart. – Aravind Oct 03 '19 at 06:27
  • What's the error you get? In postman, do you see the cors headers in the response? – acdcjunior Oct 03 '19 at 06:29
  • @acdcjunior I did not see the cors headers in the response. Error message :- ```Access to fetch at 'http://localhost:8080/api/users' from origin 'http://localhost:3001' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled``` – Aravind Oct 03 '19 at 06:36
  • Do you see the `Access-Control-Allow-Origin` header in the response received by postman? – acdcjunior Oct 03 '19 at 06:37
  • @acdcjunior ```Access-Control-Allow-Origin``` response is not there in the postman response. It's just a list of users. Response :- ```{ "username": "acme@gmail.com" }, { "username": "dummy@gmail.com" }``` – Aravind Oct 03 '19 at 06:42
  • can you provide the snippet from the spring configuration? – harisu Oct 03 '19 at 06:49
  • It's a header, do you see it in the list of headers? Do you see any header at all in the postman? If there's no header, you should be certain the problem is in the backend – acdcjunior Oct 03 '19 at 07:20
  • @acdcjunior there are three headers namely ```Content-Type```,```Content-length```,```Date```. But I dont see ```Access-Control-Allow-Origin``` – Aravind Oct 03 '19 at 07:33
  • I see. Your problem is at the backend, then. Can you post the part in your spring app that you added `@CrossOrigin`? I don't know how your app is configured, but that annotation should work. If not, perhaps [something like this](https://stackoverflow.com/a/46065200/1850609) is of help. – acdcjunior Oct 03 '19 at 07:40
  • @acdcjunior I tried doing what you said. Still getting the same error. I've also added the Spring boot snippet. – Aravind Oct 03 '19 at 08:52

2 Answers2

3

I was able to solve the problem. I had to add a CORSFilter class as follows that I found in this page :- https://stackoverflow.com/questions/23450494/how-to-enable-cross-domain-requests-on-jax-rs-web-services :-

import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.ext.Provider;

@Provider
public class CORSFilter implements ContainerResponseFilter {

    @Override
    public void filter(final ContainerRequestContext requestContext,
                       final ContainerResponseContext cres) throws IOException {
        cres.getHeaders().add("Access-Control-Allow-Origin", "*");
        cres.getHeaders().add("Access-Control-Allow-Headers", "origin, content-type, accept, authorization");
        cres.getHeaders().add("Access-Control-Allow-Credentials", "true");
        cres.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD");
        cres.getHeaders().add("Access-Control-Max-Age", "1209600");
    }

}
Aravind
  • 256
  • 1
  • 4
  • 17
1

In your configuration file if you are using WebMvcConfigurer base package you should override

    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**");
    }

harisu
  • 1,376
  • 8
  • 17