Disable OPTIONS request before POST in React

All we need is an easy explanation of the problem, so here it is.

I have a React application based on Typescript which is hosted on my PC.
I use Spring gateway to forward requests to another microservice. GET requests are working fine but for POST requests I get:

Access to XMLHttpRequest at ‘http://1.1.1.1:8080/api/support/tickets/create’ from origin ‘http://localhost:3000’ has been blocked by CORS policy: Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.

I’m hitting this Spring Cloud Gateway issue: https://github.com/spring-cloud/spring-cloud-gateway/issues/229

Spring cloud configuration:

@Bean
public RouteLocator routes(RouteLocatorBuilder builder, LoggingGatewayFilterFactory loggingFactory) {
    return builder.routes()
            .route("route_id",
                    route -> route
                            .path("/api/support/tickets/**")
                            .filters(f -> f.rewritePath("/api/support/tickets/(?<RID>.*)", "/support/tickets/${RID}"))
                            .uri("lb://merchant-hub-admin-service")
            )
            .build();
}

React code:

export async function getTicket(id: string) {
  return await axios.get(`${baseUrl}/support/tickets/ticket/${id}`);
}

export async function postTicket(
  data: TicketFullDTO
): Promise<AxiosResponse<TicketFullDTO>> {
  return await axios.post<TicketFullDTO>(
    `${baseUrl}/support/tickets/create`, data);
}

This is caused by buy-in Spring Cloud Gateway: https://github.com/spring-cloud/spring-cloud-gateway/issues/2472

I tried to implement this workaround:

@Bean
public RoutePredicateHandlerMapping tusRoutePredicateHandlerMapping(FilteringWebHandler webHandler,
                                                                    RouteLocator routeLocator,
                                                                    GlobalCorsProperties globalCorsProperties,
                                                                    Environment environment) {
    RoutePredicateHandlerMapping routePredicateHandlerMapping = new RoutePredicateHandlerMapping(webHandler,
            routeLocator, globalCorsProperties, environment);
    routePredicateHandlerMapping.setCorsProcessor(new CrackCorsProcessor());
    return routePredicateHandlerMapping;
}
import org.springframework.lang.Nullable;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.DefaultCorsProcessor;
import org.springframework.web.server.ServerWebExchange;

public class CrackCorsProcessor extends DefaultCorsProcessor {
    @Override
    public boolean process(@Nullable CorsConfiguration config, ServerWebExchange exchange) {
        return false;
    }
}

But it’s not working. Do you know how I can solve this issue?

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

Actually, CORS is a browser feature and you can disable it in the development area.

The browser should call an OPTIONS for non-matched URLs (website URL & API URL) when these two aren’t matched, the browser must call an OPTIONS call to Backend API to get allowed to continue, for production if your website URL and API are not matched you should ask your Backend or DevOps guys to settle it up but for the development area there a couple of things you can do:

  • if you are using CRA, the previous answer can help you.

  • if you are using custom configuration and you have a Webpack config file, add this line to your Webpack and it can help you:

    module.exports = {
      devServer: {
        proxy: 'http://1.1.1.1:8080',
      },
    
  • another cute way is directly disabling security in your Chrome to have an easy development, put these commands in their related OS and run, you will run a new instance of chrome without security (without OPTIONS call):

    ## macOS
    open -na Google\ Chrome --args --user-data-dir=/tmp/temporary-chrome-profile-dir --disable-web-security --disable-site-isolation-trials
    
    ## windows
    chrome.exe --disable-web-security
    
    ## linux
    google-chrome --disable-web-security
    

For more information about the last item, you can read this post.

Method 2

It seems that your issue is about local development only.

Create react app solves precisely this by supporting the dev server to serve as a proxy to your API endpoint.

Just add the API domain in the proxy key in package.json. More on this here

For production you should either host them both under the same domain (using a reverse proxy to route static content vs api request based on the URL path), or find a permanent cors friendly solution on your backend.

Cors is a browser feature and cannot be disabled. Though, when not using the POST method you can get around the preflight but you still can’t access the response body without the proper headers set

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply