Using AngularJS to call RESTFUL APIs.
When using Chrome browser on IOS, the CORS preflight request's (OPTIONS request's) Accept header gets set to */*,image/webp. The response comes back fine, but the actual GET request never gets sent. As you can see in the response, the Content-Type header gets set to image/webp, which I suspect is causing the problem in AngularJS from moving forward with the GET request.
Is my assumption correct? If so, is the solution to force the server to set the Content-Type to something else ?
Full request headers:
OPTIONS http://www.example.com:8080/resourceABC HTTP/1.1
Host: www.example.com:8080
Connection: keep-alive
Access-Control-Request-Headers: accept, authorization, origin
Access-Control-Request-Method: GET
Origin: http://www.example.com
Accept: */*,image/webp
Referer: http://www.example.com/
User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 8_1 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) CriOS/38.0.2125.67 Mobile/12B411 Safari/600.1.4
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Full response headers:
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Allow: GET,OPTIONS,HEAD
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET, POST, PUT, OPTIONS
Access-Control-Allow-Headers: X-HTTP-Method-Override, Content-Type, x-requested-with, authorization, accept
Access-Control-Max-Age: 3600
Content-Type: image/webp
Content-Length: 0
Date: Wed, 29 Oct 2014 19:26:37 GMT
Hy.
I just had the same thing here accessing a REST-Service on a different domain from AngularJS-Service.
Examining the request-headers sent to my REST-Service to see if I do allow everything that is requested from the client lead mit to the following:
Setting 'Access-Control-Allow-Headers' in a way to make sure it contains all items in 'Access-Control-Request-Headers' solved it for me.
Marc I see that in your 'Access-Control-Request-Headers' the header "origin" is requested but in your "Access-Control-Allow-Headers" it's missing.
Sending the following Response-Headers works in my case (appkey is my internal auth-key in header):
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: *
Access-Control-Allow-Headers: appkey,content-type
Hope this is clear and it helps.
Best regards,
Christian
P.S.: "Access-Control-Allow-Headers: *" did not work in my case
UPDATE:
To allow Form-Submit via POST-Request I had to add 'content-type' to 'Access-Control-Allow-Headers'
Related
We're developing a website with a REST Api (frontend in AngularJS 1.6.1, backend in Laravel 5.3).
In order to add CSRF protection, our backend needs to set a backend cookie on the client with a random string. In laravel, we return this response:
response("OK", 200)->cookie("csrf_token", "random_string");
The cookie is clearly being set with the response:
*Request headers*
POST /v1/auth/admin HTTP/1.1
Host: backend.test
Connection: keep-alive
Content-Length: 295
Accept: */*
Origin: http://frontend.test
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Referer: http://frontend.test/login
Accept-Encoding: gzip, deflate
Accept-Language: it-IT,it;q=0.8,en-US;q=0.6,en;q=0.4
*Response header*
HTTP/1.1 200 OK
Server: nginx/1.11.3
Content-Type: application/json
Transfer-Encoding: chunked
Connection: keep-alive
Access-Control-Allow-Origin: http://frontend.test
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS, PATCH
Access-Control-Allow-Headers: Origin, Content-Type, Accept, Authorization, X-Requested-With
Access-Control-Allow-Credentials: true
Cache-Control: no-cache
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 59
Date: Mon, 13 Feb 2017 11:46:16 GMT
Set-Cookie: csrf_token=random_string; expires=Sat, 12-Feb-2022 11:46:16 GMT; Max-Age=157680000; path=/; domain=http://backend.test; HttpOnly
However, when I go to the http://backend.test Url, no cookie is set (document.cookie in the console returns null).
The backend cannot see the cookie either: dd($request->cookie("csrf_token") returns null.
It doesn't work even if we omit the domain. Any ideas?
For Angular to send the cookie along with the request in a CORS (Cross Origin Resource Sharing request), you need to set, in your config with $httpProvider injected as a dependency:
.config(function ($httpProvider) {
$httpProvider.defaults.withCredentials = true;
//rest of route code
}
When you use Laravel you do not have to set the csrf cookie by yourself. Laravel automatically does this job for you.
So laravel creates automatically a cookie to store the csrf token. The name of that cookie is "XSRF-TOKEN".
Whenever I try to add custom Request headers to my $http request the headers does not show up in the Request, instead its comes under Access-Control-Request-Headers like Access-Control-Request-Headers:accept, testHeader
See below the output in chrome's network tab:
Request Headers:
OPTIONS /v/xyx/abc/query?q=SELECT%20duration%20FROM%20TimeTable HTTP/1.1
Host: example.com
Connection: keep-alive
Access-Control-Request-Method: GET
Origin: http://localhost
User-Agent: XXXXXXXX Chrome XXXXXXX
Access-Control-Request-Headers: accept, testHeader
Accept: */*
Referer: http://localhost/test/
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
Whereas,I am expecting something like:
Request Headers:
OPTIONS /v/xyx/abc/query?q=SELECT%20duration%20FROM%20TimeTable HTTP/1.1
Host: example.com
Connection: keep-alive
Access-Control-Request-Method: GET
Origin: http://localhost
User-Agent: XXXXXXXX Chrome XXXXXXX
Accept: application/json
testHeader: zdhfguwe87fg8378287efijb8
Referer: http://localhost/test/
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
How can I prevent this from happening and show the headers under Request header?
See the config of the $http service in Angularjs that I've followed:
//***TRIED BOTH OF THESE:
//***TRY#1 $http.get(url, {headers:{"Accept": "application/json", "testHeader": "zdhfguwe87fg8378287efijb8"}}).then(.......
//***TRY#2
$http({
method: 'GET',
url: url,
headers: {
"Accept": "application/json",
"testHeader": "zdhfguwe87fg8378287efijb8"
}
})
.then(
function(){
//success
console.log(arguments);
}, function(){
//fail
console.log(arguments);
});
This is expected behavior as part of the CORS-preflight, which is an OPTIONS request. Once this request succeeds, the actual GET request will be fired by the browser with the custom headers, because the server accepted them.
Only a limited set of headers is approved by default for CORS requests, therefore to add others (including your custom header), the CORS-preflight request needs to ask the server for permission with the Access-Control-Request-Headers HTTP header.
See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Preflighted_requests
It has bloody taken my 3 hours to search for the fxxking answer.
Basically it is simple, just let stupid server accept the custom header.
What I have done were adding these to .htaccess
<IfModule mod_headers.c>
Header add Access-Control-Allow-Origin "*"
Header add Access-Control-Allow-Methods "POST, GET, OPTIONS, DELETE, PUT"
Header add Access-Control-Allow-Headers "origin, x-requested-with, content-type, x-custom-header-here"
</IfModule>
Now it allows server(API) to accept ajax request from everywhere with most methods and whatever your custom header is or headers are.
it will erase error msg of 'Request header field content-type is not allowed by Access-Control-Allow-Headers in preflight response.'
fxxking all these stupid rule makers!
I have a node/express aplication with CORS enabled
When I do POST /login to my app does a redirect to /failure or /success
but always a i get a
XMLHttpRequest cannot load http://exampledomain.com/login. The request was redirected to 'http://exampledomain.com/success', which is disallowed for cross-origin requests that require preflight.
the $http do two requests to the server
OPTIONS request
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: http://otherdomain.com
Vary: Origin
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET,PUT,POST,PATCH,DELETE
Access-Control-Allow-Headers: accept, content-type
Connection: keep-alive
Options response
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: http://otherdomain.com
Vary: Origin
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET,PUT,POST,PATCH,DELETE
Access-Control-Allow-Headers: accept, content-type
Connection: keep-alive
POST request
POST /admin/login HTTP/1.1
Host: exampledomain.com
Connection: keep-alive
Content-Length: 43
Accept: application/json, text/plain, */*
Origin: http://otherdomain.com
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2267.0 Safari/537.36
Content-Type: application/json;charset=UTF-8
Referer: http://otherdomain.com
Accept-Encoding: gzip, deflate
Accept-Language: es-419,es;q=0.8
POST response
HTTP/1.1 302 Moved Temporarily
Vary: X-HTTP-Method-Override, Origin, Accept
Access-Control-Allow-Origin: http://otherdomain.com
Access-Control-Allow-Credentials: true
Location: /success
Content-Type: text/plain; charset=utf-8
Content-Length: 45
set-cookie: cookie-data; Path=/; HttpOnly
Connection: keep-alive
I think the headers are correct but something is missing about the Location header. Can help me with this
I'm doing the request with $http like follows
$http.defaults.useXDomain = true;
$http.post('http://exampledomain/login', {
username: 'user',
password: 'pass'
}).success(...).error(...)
but the success is never called
CORS is not enabled for http://exampledomain.com, thus no requests can be made to it. In fact the browser does a preflight check and since it fails the requests are not actually sent to the server at all.
The only way to change this is to enable CORS on the server for your domain.
I have a weird problem. I am testing this using Angular.js 1.2.15.
I want to send a POST request to a RESTful API backend on another domain (and I want to use $http directly, not $resource).
var mapData = {
'some': 'keys',
'other': 'keys'
}
$http.post(endPoint, mapData);
This is what happens: An OPTIONS request is sent first, with the following request headers:
OPTIONS /api/maps HTTP/1.1
Host: myhost.com
Connection: keep-alive
Access-Control-Request-Method: POST
Origin: http://0.0.0.0:9000
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/36.0.1985.125 Chrome/36.0.1985.125 Safari/537.36
Access-Control-Request-Headers: accept, content-type
Accept: */*
Referer: http://0.0.0.0:9000/
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
The response clearly shows that requests from other origins and with every method are allowed:
HTTP/1.1 204 No content
Server: Varnish
Connection: keep-alive
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: *
Access-Control-Allow-Headers: DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type
Access-Control-Max-Age: 0
Content-Type: text/plain charset=UTF-8
Accept-Ranges: bytes
Date: Tue, 02 Sep 2014 14:50:16 GMT
X-Varnish: 166874803
Age: 0
Via: 1.1 varnish
Connection: close
Cache-Control: max-age=0, private
X-Varnish-Cache: MISS
But then, the POST request is not even sent by the browser (Chromium 36), i.e. it does not show a POST request in the network tab of the dev console.
Instead, the following is shown in the console:
XMLHttpRequest cannot load http://myhost.com/api/maps. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://0.0.0.0:9000' is therefore not allowed access.
Now, what is totally weird: GET requests to the same API work, and are not preceded by an OPTIONS request (or maybe it is not shown in the network tab).
HTTP/1.1 304 Not Modified
Server: nginx/1.4.7
Content-Type: application/json; charset=utf-8
Status: 200 OK
X-UA-Compatible: IE=Edge,chrome=1
ETag: "baca3b7547fed3377088eb81fe083ff8"
X-Request-Id: b2552dc4fdef2541c841e3d5e12d337e
X-Runtime: 0.110003
X-Rack-Cache: miss
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET, POST, PUT, OPTIONS
Access-Control-Allow-Headers: DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type
Accept-Ranges: bytes
Date: Tue, 02 Sep 2014 14:54:31 GMT
X-Varnish: 166874831 166874142
Age: 6223
Via: 1.1 varnish
Connection: keep-alive
Cache-Control: max-age=0, private
X-Varnish-Cache: HIT
I really have no clue what the problem could be here. Is it Angular's implementation? Or is it a misconfiguration on the server? The guys responsible for the API told me it usually works with all their web apps.
I understand that this is a CORS problem and I am by no means an expert when it comes to that, but hey, Access-Control-Allow-Origin: * should do the trick, shouldn't it?
UPDATE: It works when using plain XMLHttpRequest:
var http = new XMLHttpRequest();
var url = endPoint;
var params = JSON.stringify(mapData);
http.open("POST", url, true);
I get a 200 back.
What is the matter here?
Nginx has to be compiled with http://nginx.org/en/docs/http/ngx_http_headers_module.html for Access-Control-Allow-Origin: * to work. Do you have this module installed?
location / {
add_header Access-Control-Allow-Origin *;
}
I'm working with an Angular app that talks to a REST api.
I have set up access-control so that my GET requests are working as expected. (The browser sends a preflight OPTIONS request and then the subsequent GET fires).
However, I am running into problems with my PUTs.
The problem is that with PUTs the options preflight seems to return okay but then the PUT never fires.
First I will show what works and then what is failing. I am using Charles to spy on the calls.
GET (working)
1) preflight request
OPTIONS /v1/account HTTP/1.1
Host api.mysite.com
Access-Control-Request-Method GET
Origin http://127.0.0.1:9000
User-Agent Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.137 Safari/537.36
Access-Control-Request-Headers accept, authorization
Accept */*
Referer http://127.0.0.1:9000/
Accept-Encoding gzip,deflate,sdch
Accept-Language en-US,en;q=0.8
2) preflight response
HTTP/1.1 200 OK
Server nginx/1.6.0
Date Wed, 21 May 2014 15:43:25 GMT
Content-Type application/octet-stream
Content-Length 0
Connection keep-alive
Access-Control-Allow-Origin *
Access-Control-Allow-Methods *
Access-Control-Allow-Headers Origin, X-Requested-With, Content-Type, Accept, Authorization, WWW-Authenticate, X-BLURR-DEBUG
Access-Control-Allow-Credentials true
3) The GET request
GET /v1/account HTTP/1.1
Host api.mysite.com
Accept application/json, text/plain, */*
Origin http://127.0.0.1:9000
User-Agent Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.137 Safari/537.36
AUTHORIZATION DFHD8D...
Referer http://127.0.0.1:9000/
Accept-Encoding gzip,deflate,sdch
Accept-Language en-US,en;q=0.8
4) The GET response
{ some json as expected }
PUT (not working)
Now, here is the PUT that seems to return an okay preflight response but never gets to the PUT request:
1) preflight request
OPTIONS /v1/account HTTP/1.1
Host api.mysite.com
Access-Control-Request-Method PUT
Origin http://127.0.0.1:9000
User-Agent Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.137 Safari/537.36
Access-Control-Request-Headers accept, authorization, content-type
Accept */*
Referer http://127.0.0.1:9000/
Accept-Encoding gzip,deflate,sdch
Accept-Language en-US,en;q=0.8
2) preflight response
HTTP/1.1 200 OK
Server nginx/1.6.0
Date Wed, 21 May 2014 15:51:41 GMT
Content-Type application/octet-stream
Content-Length 0
Connection keep-alive
Access-Control-Allow-Origin *
Access-Control-Allow-Methods *
Access-Control-Allow-Headers Origin, X-Requested-With, Content-Type, Accept, Authorization, WWW-Authenticate, X-BLURR-DEBUG
Access-Control-Allow-Credentials true
The problem
It seems that the preflight response for the PUT is okay, so I'm not sure why the browser never sends the actual PUT request.
Any help or direction would be great! Thank you
The * (star) is not a valid value for the Access-Control-Allow-Methods header. You need to list the actual methods (e.g. GET, PUT). It seems like although the preflight response is successful from the server, the browser is probably still rejecting the preflight and not sending the actual request. Check the browser's console log for any errors.