[ACCEPTED]-AngularJS performs an OPTIONS HTTP request for a cross-origin resource-preflight

Accepted answer
Score: 228

OPTIONS request are by no means an AngularJS 29 bug, this is how Cross-Origin Resource Sharing 28 standard mandates browsers to behave. Please 27 refer to this document: https://developer.mozilla.org/en-US/docs/HTTP_access_control, where in the "Overview" section 26 it says:

The Cross-Origin Resource Sharing 25 standard works by adding new HTTP headers 24 that allow servers to describe the set of 23 origins that are permitted to read that 22 information using a web browser. Additionally, for 21 HTTP request methods that can cause side-effects 20 on user data (in particular; for HTTP 19 methods other than GET, or for POST usage 18 with certain MIME types). The specification 17 mandates that browsers "preflight" the 16 request, soliciting supported methods from 15 the server with an HTTP OPTIONS request 14 header, and then, upon "approval" from the 13 server, sending the actual request with 12 the actual HTTP request method. Servers 11 can also notify clients whether "credentials" (including 10 Cookies and HTTP Authentication data) should 9 be sent with requests.

It is very hard 8 to provide a generic solution that would 7 work for all the WWW servers as setup will 6 vary depending on the server itself and 5 HTTP verbs that you intend to support. I 4 would encourage you to get over this excellent 3 article (http://www.html5rocks.com/en/tutorials/cors/) that has much more details on 2 the exact headers that needs to be sent 1 by a server.

Score: 70

For Angular 1.2.0rc1+ you need to add a 12 resourceUrlWhitelist.

1.2: release version they 11 added a escapeForRegexp function so you 10 no longer have to escape the strings. You 9 can just add the url directly

'http://sub*.assets.example.com/**' 

make sure 8 to add ** for sub folders. Here is a working 7 jsbin for 1.2: http://jsbin.com/olavok/145/edit


1.2.0rc: If you are still on a rc 6 version, the Angular 1.2.0rc1 the solution 5 looks like:

.config(['$sceDelegateProvider', function($sceDelegateProvider) {
     $sceDelegateProvider.resourceUrlWhitelist(['self', /^https?:\/\/(cdn\.)?yourdomain.com/]);
 }])

Here is a jsbin example where 4 it works for 1.2.0rc1: http://jsbin.com/olavok/144/edit


Pre 1.2: For older versions 3 (ref http://better-inter.net/enabling-cors-in-angular-js/) you need to add the following 2 lines 2 to your config:

$httpProvider.defaults.useXDomain = true;
delete $httpProvider.defaults.headers.common['X-Requested-With'];

Here is a jsbin example where 1 it works for pre 1.2 versions: http://jsbin.com/olavok/11/edit

Score: 62

NOTE: Not sure it works with the latest version 3 of Angular.

ORIGINAL:

It's also possible to override 2 the OPTIONS request (was only tested in 1 Chrome):

app.config(['$httpProvider', function ($httpProvider) {
  //Reset headers to avoid OPTIONS request (aka preflight)
  $httpProvider.defaults.headers.common = {};
  $httpProvider.defaults.headers.post = {};
  $httpProvider.defaults.headers.put = {};
  $httpProvider.defaults.headers.patch = {};
}]);
Score: 34

Your service must answer an OPTIONS request with 1 headers like these:

Access-Control-Allow-Origin: [the same origin from the request]
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: [the same ACCESS-CONTROL-REQUEST-HEADERS from request]

Here is a good doc: http://www.html5rocks.com/en/tutorials/cors/#toc-adding-cors-support-to-the-server

Score: 20

The same document says

Unlike simple requests 22 (discussed above), "preflighted" requests 21 first send an HTTP OPTIONS request header 20 to the resource on the other domain, in 19 order to determine whether the actual request 18 is safe to send. Cross-site requests are 17 preflighted like this since they may have 16 implications to user data. In particular, a 15 request is preflighted if:

It uses methods 14 other than GET or POST. Also, if POST is 13 used to send request data with a Content-Type 12 other than application/x-www-form-urlencoded, multipart/form-data, or 11 text/plain, e.g. if the POST request sends 10 an XML payload to the server using application/xml 9 or text/xml, then the request is preflighted.

It 8 sets custom headers in the request (e.g. the 7 request uses a header such as X-PINGOTHER)

When 6 the original request is Get with no custom 5 headers, the browser should not make Options 4 request which it does now. The problem is 3 it generates a header X-Requested-With which 2 forces the Options request. See https://github.com/angular/angular.js/pull/1454 on how 1 to remove this header

Score: 10

This fixed my problem:

$http.defaults.headers.post["Content-Type"] = "text/plain";

0

Score: 10

If you are using a nodeJS server, you can 2 use this library, it worked fine for me 1 https://github.com/expressjs/cors

var express = require('express')
  , cors = require('cors')
  , app = express();

app.use(cors());

and after you can do an npm update.

Score: 5

Here is the way I fixed this issue on ASP.NET

  • First, you 5 should add the nuget package Microsoft.AspNet.WebApi.Cors

  • Then modify 4 the file App_Start\WebApiConfig.cs

    public static class WebApiConfig    
    {
       public static void Register(HttpConfiguration config)
       {
          config.EnableCors();
    
          ...
       }    
    }
    
  • Add 3 this attribute on your controller class

    [EnableCors(origins: "*", headers: "*", methods: "*")]
    public class MyController : ApiController
    {  
        [AcceptVerbs("POST")]
        public IHttpActionResult Post([FromBody]YourDataType data)
        {
             ...
             return Ok(result);
        }
    }
    
  • I 2 was able to send json to the action by this 1 way

    $http({
            method: 'POST',
            data: JSON.stringify(data),
            url: 'actionurl',
            headers: {
                'Content-Type': 'application/json; charset=UTF-8'
            }
        }).then(...)
    

Reference : Enabling Cross-Origin Requests in ASP.NET Web API 2

Score: 2

Somehow I fixed it by changing

<add name="Access-Control-Allow-Headers" 
     value="Origin, X-Requested-With, Content-Type, Accept, Authorization" 
     />

to

<add name="Access-Control-Allow-Headers" 
     value="Origin, Content-Type, Accept, Authorization" 
     />

0

Score: 0

Perfectly described in pkozlowski's comment. I 6 had working solution with AngularJS 1.2.6 5 and ASP.NET Web Api but when I had upgraded 4 AngularJS to 1.3.3 then requests failed.

  • Solution 3 for Web Api server was to add handling of 2 the OPTIONS requests at the beginning of 1 configuration method (more info in this blog post):

    app.Use(async (context, next) =>
    {
        IOwinRequest req = context.Request;
        IOwinResponse res = context.Response;
        if (req.Path.StartsWithSegments(new PathString("/Token")))
        {
            var origin = req.Headers.Get("Origin");
            if (!string.IsNullOrEmpty(origin))
            {
                res.Headers.Set("Access-Control-Allow-Origin", origin);
            }
            if (req.Method == "OPTIONS")
            {
                res.StatusCode = 200;
                res.Headers.AppendCommaSeparatedValues("Access-Control-Allow-Methods", "GET", "POST");
                res.Headers.AppendCommaSeparatedValues("Access-Control-Allow-Headers", "authorization", "content-type");
                return;
            }
        }
        await next();
    });
    
Score: 0

If you are using Jersey for REST API's you 6 can do as below

You don't have to change 5 your webservices implementation.

I will explain 4 for Jersey 2.x

1) First add a ResponseFilter 3 as shown below

import java.io.IOException;

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

public class CorsResponseFilter implements ContainerResponseFilter {

@Override
public void filter(ContainerRequestContext requestContext,   ContainerResponseContext responseContext)
    throws IOException {
        responseContext.getHeaders().add("Access-Control-Allow-Origin","*");
        responseContext.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");

  }
}

2) then in the web.xml , in 2 the jersey servlet declaration add the below 1

    <init-param>
        <param-name>jersey.config.server.provider.classnames</param-name>
        <param-value>YOUR PACKAGE.CorsResponseFilter</param-value>
    </init-param>
Score: 0

I gave up trying to fix this issue.

My IIS 14 web.config had the relevant "Access-Control-Allow-Methods" in it, I 13 experimented adding config settings to my 12 Angular code, but after burning a few hours 11 trying to get Chrome to call a cross-domain 10 JSON web service, I gave up miserably.

In 9 the end, I added a dumb ASP.Net handler 8 webpage, got that to call my JSON web service, and 7 return the results. It was up and running 6 in 2 minutes.

Here's the code I used:

public class LoadJSONData : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        context.Response.ContentType = "text/plain";

        string URL = "......";

        using (var client = new HttpClient())
        {
            // New code:
            client.BaseAddress = new Uri(URL);
            client.DefaultRequestHeaders.Accept.Clear();
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            client.DefaultRequestHeaders.Add("Authorization", "Basic AUTHORIZATION_STRING");

            HttpResponseMessage response = client.GetAsync(URL).Result;
            if (response.IsSuccessStatusCode)
            {
                var content = response.Content.ReadAsStringAsync().Result;
                context.Response.Write("Success: " + content);
            }
            else
            {
                context.Response.Write(response.StatusCode + " : Message - " + response.ReasonPhrase);
            }
        }
    }

    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

And 5 in my Angular controller...

$http.get("/Handlers/LoadJSONData.ashx")
   .success(function (data) {
      ....
   });

I'm sure there's 4 a simpler/more generic way of doing this, but 3 life's too short...

This worked for me, and 2 I can get on with doing normal work now 1 !!

Score: 0

For an IIS MVC 5 / Angular CLI ( Yes, I 9 am well aware your problem is with Angular 8 JS ) project with API I did the following:

web.config under 7 <system.webServer> node

    <staticContent>
      <remove fileExtension=".woff2" />
      <mimeMap fileExtension=".woff2" mimeType="font/woff2" />
    </staticContent>
    <httpProtocol>
      <customHeaders>
        <clear />
        <add name="Access-Control-Allow-Origin" value="*" />
        <add name="Access-Control-Allow-Headers" value="Content-Type, atv2" />
        <add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS"/>
      </customHeaders>
    </httpProtocol>

global.asax.cs

protected void Application_BeginRequest() {
  if (Request.Headers.AllKeys.Contains("Origin", StringComparer.OrdinalIgnoreCase) && Request.HttpMethod == "OPTIONS") {
    Response.Flush();
    Response.End();
  }
}

That should fix your issues for both 6 MVC and WebAPI without having to do all 5 the other run around. I then created an 4 HttpInterceptor in the Angular CLI project 3 that automatically added in the the relevant 2 header information. Hope this helps someone 1 out in a similar situation.

Score: 0

Little late to the party,

If you are using 8 Angular 7 (or 5/6/7) and PHP as the API 7 and still getting this error, try adding 6 following header options to the end point 5 (PHP API).

 header("Access-Control-Allow-Origin: *");
 header("Access-Control-Allow-Methods: PUT, GET, POST, PUT, OPTIONS, DELETE, PATCH");
 header("Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Authorization");

Note : What only requires is Access-Control-Allow-Methods. But, I 4 am pasting here other two Access-Control-Allow-Origin and Access-Control-Allow-Headers, simply 3 because you will need all of these to be 2 properly set in order Angular App to properly 1 talk to your API.

Hope this helps someone.

Cheers.

More Related questions