'POST changes to OPTIONS + Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header

XMLHttpRequest cannot load http://xxx.xxx. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access. The response had HTTP status code 500.

I am trying to send a xml soap with ajax but gives me that error. I have tried many option but nothing seems to work, here is the code:

var soapMessage =
                '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsdl="http://xxx.xxx/">'+
                '<soapenv:Header/>'+
                '<soapenv:Body>'+
                   '<wsdl:test1>'+
                      '<PUI>12345</PUI>'+
                   '</wsdl:test1>'+
               ' </soapenv:Body>'+
             '</soapenv:Envelope>';

            $.ajax({
                url: 'http://xxx.xxx', 
                type: 'POST',
                dataType: 'xml', 
                data: soapMessage, 
                crossDomain: true,
                processData: false,
                contentType: 'text/xml; charset=\"utf-8\"',
                headers: {
                    SOAPAction: "http://xxx.xxx"
                },
                success: function (msg, data) {
                    alert(msg);

                },
                error: function (msg, data) {
                    alert("Error");
                }
            });

what am I doing wrong here? I send a POST action but it read it as OPTION. How to fix this?

I use Boomerang Rest and Soap Client to test this service and it gives me response correctly. When I use my own program as above it gives me XMLHttpRequest cannot load http://xxxxx" error. I am using apache tomcat 6.0 and using a Java web Application for the code



Solution 1:[1]

You’re doing that request cross-origin, so the server you’re making the request to must send an Access-Control-Allow-Origin response header to indicate it allows cross-origin requests.

See https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS for details.

For security reasons, browsers restrict cross-origin HTTP requests initiated from within scripts. For example, XMLHttpRequest and Fetch follow the same-origin policy. So, a web application using XMLHttpRequest or Fetch could only make HTTP requests to its own domain.

And the reason an OPTIONS request happens is that when you send a cross-origin request with a Content-Type header that has a value other than application/x-www-form-urlencoded, multipart/form-data, or text/plain, your browser first does a CORS preflight check.

Your request sends Content-Type: text/xml; charset="utf-8", so that causes a preflight.

As far as workarounds if the server you’re sending the request to is not one that you control and can configure, you can use an open reverse proxy like https://cors-anywhere.herokuapp.com/.

The way it works is that instead of sending your request directly to http://xxx.xxx, you send it instead to https://cors-anywhere.herokuapp.com/http://xxx.xxx and that proxies your request and responds with Access-Control-Allow-Origin and other expected CORS headers.

Of course you need to realize that if your request contains any credentials, you’d be exposing those to the maintainers of cors-anywhere.herokuapp.com.

Solution 2:[2]

My work around for this was to write a filter that appends the origin as an accepted origin onto any options request and added it to whatever servlet that would need to accept such requests. Here's my implementation:

public class CorsFilter implements Filter {

    private static List<String> validServers = Arrays.asList([you need to fill this in with whatever sites you want to allow access]);

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        if (servletRequest instanceof HttpServletRequest) {
            HttpServletRequest request = (HttpServletRequest) servletRequest;
            HttpServletResponse response = (HttpServletResponse) servletResponse;
            String origin = request.getHeader("Origin");
            if (StringUtils.isNotBlank(origin)) { //this is a cors request
                boolean hasPrefix = origin.contains("/");
                boolean hasPort = origin.contains(":");
                String serverAlias = origin.substring(hasPrefix ? origin.lastIndexOf("/") + 1 : 0, hasPort ? origin.lastIndexOf(":") : origin.length());
                if (validServers.contains(serverAlias)) {
                    response.setHeader("Access-Control-Allow-Credentials", "true");
                    response.setHeader("Access-Control-Allow-Methods", "OPTIONS, POST, GET, PUT, DELETE");
                    response.setHeader("Access-Control-Allow-Origin", origin);
                    response.setHeader("Access-Control-Allow-Headers", "Content-Type");
                    //credentials are not sent on options requests, kick out here so that the access control headers and nothing else can be returned
                    if ("OPTIONS".equals(request.getMethod())) {
                        response.setStatus(200);
                        return;
                    }
                } else {
                    response.sendError(HttpStatus.SC_FORBIDDEN);
                    response.flushBuffer();
                    return;
                }
            }
        }

        filterChain.doFilter(servletRequest, servletResponse);
    }


    @Override
    public void destroy() {
    }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Taugenichts