I am trying to make a http get request from AngularJS. It is only working in the Internet Explorer 11. When I try to make a POST Call in Chrome, Firefox or Edge it is not working.
I get the following error:
CORS Error in Chrome
This is my AngularJS Call:
$http({
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
url: URL
}).then(function successCallback(response) {
console.log(response.data);
}, function errorCallback(response) {
console.log(response.status);
console.log(response.headers);
console.log(response.config);
});
As backend I have a Java Spring MVC Application and I have set the following headers:
HttpHeaders responseHeader = new HttpHeaders();
responseHeader.set("Access-Control-Allow-Origin","*");
responseHeader.set("Access-Control-Allow-Headers:","Content-Type");
responseHeader.set("Access-Control-Allow-Methods","POST, GET, PUT, OPTIONS, DELETE");
responseHeader.set("Content-Type","application/json");
But I saw that angular never reaches the backend when I set a breakpoint! This is very confusing, I already searched a lot in the internet and tried to changed the default AngularJS Content-Type header:
$http.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8';
I tried also to change the Content-Type in the POST-Request to "application/x-www-form-urlencoded" - with no effect.
Note: The backend and the frontend runs on the same dev machine. The only difference is the port, that's why I am confused that I get a CORS Error at all...
I have this problem only with POST-Requests; GET works like a Charm
Edit 1:
I thought that this is a Problem with cors and that i use different ports, but is it possible that I can turn that off?
Edit 2:
Now i try to get it to work with a local tomcat. Now i see in the Tomcat the request will reach the server:
<< Client IP>> - - [28/Jun/2017:13:43:24 +0200] "OPTIONS <<URL>> HTTP/1.1" 403 -
The Response is now HTTP 403. In Browsers network tab i can see the following request header:
Host: <<backend ip>>:8080
User-Agent: <<user agent>>
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: de,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate
Access-Control-Request-Method: POST
Access-Control-Request-Headers: access-control-allow-origin,content-type
Origin: http://<<Ip of host>>
Connection: keep-alive
The Response of that request header is the following:
Content-Type: text/plain
Content-Length: 0
Date: Wed, 28 Jun 2017 11:43:24 GMT
I have set the cors filter like the example in the tomcat documentation: http://tomcat.apache.org/tomcat-7.0-doc/config/filter.html#CORS_Filter
When i make a get request, it is still working and all CORS headers are present.
I think there is something wrong with the first OPTIONS request. I dont know why the tomcat do not set the CORS header when the OPTIONS request comes.
In your Spring Controller, add #CrossOrigin('http://localhost:8080')
annotation. Of course, substitute out the port of your angular application.
Since you are getting 400 (not authorized) error, the request is not reaching the service itself. Most probably the request domain is blocked, you may see if XHR requests are allowed and check cross origin policy if needed.
I figured it out. I made a completly new angularjs testwebsite. This is my working angularjs Code:
var req = {
method: 'POST',
url: 'myURL',
headers: {
'Content-Type': undefined
},
data: "JSON BODY DATA"
}
$http(req).then(function successCallback(response) {
console.log(response.data);
}, function errorCallback(response) {
console.log(response.data);
});
}]);
I deleted anything that changed the default angularjs headers.
The headers of my Spring MVC Application are the following:
HttpHeaders responseHeader = new HttpHeaders();
responseHeader.set("Access-Control-Allow-Origin","http://<<clientip>>");
responseHeader.set("Access-Control-Allow-Headers:","Content-Type");
responseHeader.set("Access-Control-Allow-Methods","POST, GET, PUT, OPTIONS, DELETE");
responseHeader.set("Content-Type","application/json");
I dont know the exactly cause why it is working now. Maybe it was because i modified the default headers in AngularJS.
Related
I have some problems with sending a POST request to my REST-API.
The problem is, when I send it from a react application, it shows me this error in the debug console of firefox.
The funny thing is, that it works perfectly fine when sending the request with postman.
This is the code i use to make the request:
let apiURL = API_URL_BASE + "/api/authenticate"
let requestBody = JSON.stringify(
{
"username": this.getEnteredLoginUsername(),
"password": this.getEnteredLoginPassword()
}
);
let headerData = new Headers();
headerData.append('Accept', '*');
headerData.append("Access-Control-Allow", "*");
headerData.append('Content-Type', 'application/json');
headerData.append('Access-Control-Allow-Origin', '*');
headerData.append("Access-Control-Allow-Methods", "GET,HEAD,OPTIONS,POST,PUT");
headerData.append("Access-Control-Allow-Headers", "*");
let requestOptions = {
method: 'POST',
mode: 'cors',
redirect: 'follow',
body: requestBody,
headers: headerData
}
this.setState({loadingData: true});
fetch(apiURL, requestOptions).then( response => {
let responseStatus = response.status;
response.json().then( responseJSON => {
});
});
I hope someone can help me with this.
This is the error shown by firefox console: Image
You do seem to have a correct request header from the client-side, i.e the browser, but your server that is hosting the API must also send a response to the client back indicating that it allows cross-origin requests, Otherwise browser would not proceed ahead with your request. Setting cors headers from the server would depend on what framework you're using for the backend. In fact you need to add those cors header you've added here to the server code.
A sample response header would look like this :
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2
Access-Control-Allow-Origin: * (Note: * means this will allow all domains to request to your server)
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml
For express, you can follow this link.
More on CORS here
Here is the problem,
I try to get an authentication token from my alfresco community server with a js (angular 4) script. The request seems to be blocked by "CORS header missing"...
The login/password are ok since I can try the login URL directly in my browser and I get the expected result (xml) like this :
https://www.my-url.com/alfresco/service/api/login?u=username&pw=password
I get this as a result :
Now, I come to my web app and I need to retrieve this ticket from an http call, like this (with angular 4 service) :
getTicket(user: User): Observable<boolean>{
let headers = new Headers();
headers.append('Access-Control-Allow-Origin','*');
return this.http.post('https://www.my-url.com/alfresco/service/api/login',{'u':'username','pw':'password'},{headers:headers}).map(response => {
console.log('response : '+JSON.stringify(response));
});
}
I hardcoded the u/pw variables, I also tried to name them "user" & "password", and I tried GET method too, but nothing changes, the only thing I get back is a Cors problem. Result in console :
OPTIONS XHR https://www.my-url.com/alfresco/service/api/login
[HTTP/1.1 200 OK 39 ms]
Cross-Origin Request Blocked: The Same Origin Policy disallows reading
the remote resource ... Reason: CORS header
'Access-Control-Allow-Origin' missing.
Request data :
Response Headers :
Allow: "OPTIONS, GET, POST"
Content-Length: "0"
Date : "Wed, 11 Oct 2017 14:37:45 GMT"
Server: "Apache-Coyote/1.1"
Request Headers :
Host : "www.my-url.com"
User-Agent : "Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0"
Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
Accept-Language : "en-US,en;q=0.5"
Accept-Encoding: "gzip, deflate"
Access-Control-Request-Method: "POST"
Access-Control-Request-Headers: "access-control-allow-origin"
Origin: "http://localhost"
Connection: "keep-alive"
I also tried removing the headers I manually create for the request, but no effect...
How To Enable CORS in alfresco V5.2 ? Put enablecors-1.0.jar
in modules/platform folder.
I have been trying to get hold of the cookies I am setting in the server using PlayFramework:
response().setHeader(SET_COOKIE, AppConstants.COOKIE_USER_SESSIONID+"="+appSession.getSid());
in the angular app, but am not able to.
If I use the Advanced Rest client, I get the SetCookie header and the cookies get set in the call that follows. However when I call the same api through my angular app, I am not able to get the header in the response and hence no cookies for the app.
Here's what I have already tried:
renaming the environment from localhost to xyz.com since I read on multiple answers that cookies do not work at localhost.
Tried the api form a separate rest client, was able to fetch the header successfully, so the API looks fine to me.
There should be some catch in angular code which I am not able to figure out. Any help on this is highly appreciated. Thanks.
Angular code snippets:
LoginController:
angular.module('wratApp')
.controller('LoginCtrl', function (postService, wratSettings, wratRoutes, $location, $cookies) {
$("#header").hide();
this.user= {};
this.logUserin = function(){
console.log("User: " + this.user.email + " & pwd: " + this.user.pwd);
var payLoad = {};
payLoad.ldap = this.user.email;
payLoad.pwd = this.user.pwd;
postService.postPromise(wratRoutes(wratSettings).POST_USER_LOGIN(),payLoad)
.then(function(){ //login success
console.log("login success");
$location.path(wratRoutes().CLIENT_HOME());
}, function(){ //error in login
console.log("Login failed");
})
;
}
});
postService:
angular.module('wratApp')
.factory('postService', function ($http, $q, $cookies, $location) {
function postService() {
var self = this;
self.postPromise = function (uri,payload){
var deferred = $q.defer();
$http.post(uri,payload)
.success(function(data, status, headers, config){
console.log("Got response cookies: "+$cookies.getAll());
deferred.resolve(data);
})
.error(function(data, status, headers, config){
if(status == 401){
angular.forEach($cookies.getAll(), function (v, k) {
$cookies.remove(k);
});
$location.path('/login');
}else{
deferred.reject(data);
}
});
return deferred.promise;
}
}
return new postService();
});
Now, my login succeeds but without any cookies being set by the server. Also, the PlaySession cookie which is somehow visible in the debugger(the manually set ones are even absent from debugger), is not in the angular $cookies variable(refer image below).
Please suggest how can I resolve this. Thanks.
Update 1:
If I run the Play server not in debug mode, the cookies are appropriately being sent by the server. It's an issue with the angular app where in the following call, it's not transferring the values form the Set-Cookie header to the cookies for the next call. It might be some silly mistake on my end too. Please see if you can help me figure out.
Server response /login:
HTTP/1.1 200 OK
Vary: Origin
Set-Cookie: sid=1e6823e24554598b0521ca5f64d7746b; Path=/
Set-Cookie: PLAY_SESSION=953d9d5730bf1b77cccaadef6b78c209e59d924b-sid=1e6823e24554598b0521ca5f64d7746b; Path=/; HTTPOnly
Content-Type: application/json; charset=utf-8
Access-Control-Allow-Origin: http://wrat.com:9009
Access-Control-Allow-Methods: OPTIONS, GET, POST
Access-Control-Allow-Credentials: true
Date: Thu, 24 Sep 2015 22:27:03 GMT
Content-Length: 429
The following request which should have cookies set, but no cookies there :-( :
GET /products/all HTTP/1.1
Host: wrat.com:9000
Accept: application/json, text/plain, */*
Origin: http://wrat.com:9009
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.93 Safari/537.36
Referer: http://wrat.com:9009/
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
Taking off the Play framework tag since it's not a play issue anymore.
Adding the withCredentials option to the $http calls worked for me and resolved the issue.
$http.post(uri,payload, {withCredentials: true})
The issue is with the ajax calls where if this withCredentials flag is not truthy at both server and client, the cookies set by server are not maintained for cross-origin calls. Hence it was totally ignoring the cookies I was setting from the server.
Text from following question helped resolve the problem:
Why is jquery's .ajax() method not sending my session cookie?
from:
I am operating in cross-domain scenario. During login remote server is
returning Set-Cookie header along with
Allow-Access-Control-Credentials set to true.
The next ajax call to remote server should use this cookie.
CORS's Access-Control-Allow-Credentials is there to allow cross-domain
logging. Check https://developer.mozilla.org/En/HTTP_access_control
for examples.
For me it seems like a bug in JQuery (or at least feature-to-be in
next version).
UPDATE:
Cookies are not set automatically from AJAX response (citation:
http://aleembawany.com/2006/11/14/anatomy-of-a-well-designed-ajax-login-experience/)
Why?
You cannot get value of the cookie from response to set it manually
(http://www.w3.org/TR/XMLHttpRequest/#dom-xmlhttprequest-getresponseheader)
I'm confused..
There should exist a way to ask jquery.ajax() to set
XMLHttpRequest.withCredentials = "true" parameter.
ANSWER: You should use xhrFields param of
http://api.jquery.com/jQuery.ajax/
The example in the documentation is:
$.ajax({ url: a_cross_domain_url, xhrFields: {
withCredentials: true } }); It's important as well that server answers correctly to this request. Copying here great comments
from #Frédéric and #Pebbl:
Important note: when responding to a credentialed request, server must
specify a domain, and cannot use wild carding. The above example would
fail if the header was wildcarded as: Access-Control-Allow-Origin: *
So when the request is:
Origin: http://foo.example Cookie: pageAccess=2 Server should respond
with:
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Credentials: true
[payload] Otherwise payload won't be returned to script. See:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials
Okay, I've looked all over for this. Basically we're using $http request that ARE cross domain requests. Our server allows the domain and when a request returns 200, everything is OK. However, anytime our server returns an error, 500, 401, whatever, Angular thinks it's a CORS issue.
I debugged the response with Fiddler to verify my server IS returning a 500, yet Angular chokes on it.
Here's the request:
var params = {
url: "fakehost/example",
method: 'GET',
headers: {
"Authorization": "Basic encodedAuthExample"
}
};
$http(params).then(
function (response) { // success
},
function (error) { // error
// error.status is always 0, never includes data error msg
});
Then in the console, I will see this:
XMLHttpRequest cannot load fakehost/example. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'mylocalhost:5750' is therefore not allowed access.
Yet, in fiddler, the true response is:
HTTP/1.1 500 Internal Server Error
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Tue, 26 Aug 2014 12:18:17 GMT
Content-Length: 5683
{"errorId":null,"errorMessage":"Index was outside the bounds of the array.","errorDescription":"Stack trace here"}
I'm on AngularJS v1.2.16
I think I found an answer, looks like you will have to inject in your asp.net pipeline the correct CORS headers, as mentioned here.
I am a novice to angular.js, and I am trying to add some headers to a request:
var config = {headers: {
'Authorization': 'Basic d2VudHdvcnRobWFuOkNoYW5nZV9tZQ==',
'Accept': 'application/json;odata=verbose'
}
};
$http.get('https://www.example.com/ApplicationData.svc/Malls(1)/Retailers', config).success(successCallback).error(errorCallback);
I've looked at all the documentation, and this seems to me like it should be correct.
When I use a local file for the URL in the $http.get, I see the following HTTP request on the network tab in Chrome:
GET /app/data/offers.json HTTP/1.1
Host: www.example.com
Connection: keep-alive
Cache-Control: max-age=0
If-None-Match: "0f0abc9026855b5938797878a03e6889"
Authorization: Basic Y2hhZHN0b25lbWFuOkNoYW5nZV9tZQ==
Accept: application/json;odata=verbose
X-Requested-With: XMLHttpRequest
If-Modified-Since: Sun, 24 Mar 2013 15:58:55 GMT
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22
X-Testing: Testing
Referer: http://www.example.com/app/index.html
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
As you can see, both of the headers were added correctly. But when I change the URL to the one shown in the $http.get above (except using the real address, not example.com), then I get:
OPTIONS /ApplicationData.svc/Malls(1) HTTP/1.1
Host: www.datahost.net
Connection: keep-alive
Access-Control-Request-Method: GET
Origin: http://mpon.site44.com
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22
Access-Control-Request-Headers: accept, origin, x-requested-with, authorization, x-testing
Accept: */*
Referer: http://mpon.site44.com/app/index.html
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
The only difference in code between these two is one is for the first the URL is a local file, and for the second the URL is a remote server. If you look at the second Request header, there is no Authentication header, and the Accept appears to be using a default instead of the one specified. Also, the first line now says OPTIONS instead of GET (although Access-Control-Request-Method is GET).
Any idea what is wrong with the above code, or how to get the additional headers included using when not using a local file as a data source?
I took what you had, and added another X-Testing header
var config = {headers: {
'Authorization': 'Basic d2VudHdvcnRobWFuOkNoYW5nZV9tZQ==',
'Accept': 'application/json;odata=verbose',
"X-Testing" : "testing"
}
};
$http.get("/test", config);
And in the Chrome network tab, I see them being sent.
GET /test HTTP/1.1
Host: localhost:3000
Connection: keep-alive
Accept: application/json;odata=verbose
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_3) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22
Authorization: Basic d2VudHdvcnRobWFuOkNoYW5nZV9tZQ==
X-Testing: testing
Referer: http://localhost:3000/
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
Are you not seeing them from the browser, or on the server? Try the browser tooling or a debug proxy and see what is being sent out.
Basic authentication using HTTP POST method:
$http({
method: 'POST',
url: '/API/authenticate',
data: 'username=' + username + '&password=' + password + '&email=' + email,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"X-Login-Ajax-call": 'true'
}
}).then(function(response) {
if (response.data == 'ok') {
// success
} else {
// failed
}
});
...and GET method call with header:
$http({
method: 'GET',
url: '/books',
headers: {
'Authorization': 'Basic d2VudHdvcnRobWFuOkNoYW5nZV9tZQ==',
'Accept': 'application/json',
"X-Login-Ajax-call": 'true'
}
}).then(function(response) {
if (response.data == 'ok') {
// success
} else {
// failed
}
});
If you want to add your custom headers to ALL requests, you can change the defaults on $httpProvider to always add this header…
app.config(['$httpProvider', function ($httpProvider) {
$httpProvider.defaults.headers.common = {
'Authorization': 'Basic d2VudHdvcnRobWFuOkNoYW5nZV9tZQ==',
'Accept': 'application/json;odata=verbose'
};
}]);
my suggestion will be add a function call settings like this
inside the function check the header which is appropriate for it. I am sure it will definitely work. it is perfectly working for me.
function getSettings(requestData) {
return {
url: requestData.url,
dataType: requestData.dataType || "json",
data: requestData.data || {},
headers: requestData.headers || {
"accept": "application/json; charset=utf-8",
'Authorization': 'Bearer ' + requestData.token
},
async: requestData.async || "false",
cache: requestData.cache || "false",
success: requestData.success || {},
error: requestData.error || {},
complete: requestData.complete || {},
fail: requestData.fail || {}
};
}
then call your data like this
var requestData = {
url: 'API end point',
data: Your Request Data,
token: Your Token
};
var settings = getSettings(requestData);
settings.method = "POST"; //("Your request type")
return $http(settings);
What you see for OPTIONS request is fine. Authorisation headers are not exposed in it.
But in order for basic auth to work you need to add: withCredentials = true; to your var config.
From the AngularJS $http documentation:
withCredentials - {boolean} - whether to to set the withCredentials
flag on the XHR object. See requests with credentials for more
information.
And what's the answer from the server? It should reply a 204 and then really send the GET you are requesting.
In the OPTIONS the client is checking if the server allows CORS requests. If it gives you something different than a 204 then you should configure your server to send the correct Allow-Origin headers.
The way you are adding headers is the right way to do it.
Chrome is preflighting the request to look for CORS headers. If the request is acceptable, it will then send the real request. If you're doing this cross-domain, you will simply have to deal with it or else find a way to make the request non-cross-domain. This is by design.
Unlike simple requests (discussed above), "preflighted" requests first
send an HTTP request by the OPTIONS method to the resource on the
other domain, in order to determine whether the actual request is safe
to send. Cross-site requests are preflighted like this since they may
have implications to user data. In particular, a request is
preflighted if:
It uses methods other than GET, HEAD or POST. Also, if POST is used to
send request data with a Content-Type other than
application/x-www-form-urlencoded, multipart/form-data, or text/plain,
e.g. if the POST request sends an XML payload to the server using
application/xml or text/xml, then the request is preflighted. It sets
custom headers in the request (e.g. the request uses a header such as
X-PINGOTHER)
Ref: AJAX in Chrome sending OPTIONS instead of GET/POST/PUT/DELETE?
You are just adding a header which server does not allow.
eg - your server is set up CORS to allow these headers only (accept,cache-control,pragma,content-type,origin)
and in your http request you are adding like this
headers: {
'Authorization': 'Basic d2VudHdvcnRobWFuOkNoYW5nZV9tZQ==',
'Accept': 'application/json',
'x-testing': 'testingValue'
}
then the Server will reject this request since (Authorization and x-testing) are not allowed.
This is server side configuration.
And there is nothing to do with HTTP Options, it is just a preflight to server which is from different domain to check if server will allow actual call or not.
For me the following explanatory snippet worked. Perhaps you shouldn't use ' for header name?
{
headers: {
Authorization: "Basic " + getAuthDigest(),
Accept: "text/plain"
}
}
I'm using $http.ajax(), though I wouldn't expect that to be a game changer.