I'm actually trying to implement a file upload system between my client side angularjs app and my server but i'm having difficulties to implement this feature.
The problem seems to come from the preflight OPTIONS request sent from Chrome.
When I test my route with postman everything work just fine.
Here is a few screen shots of the postman request execution:
First part of postman example
Second part of postman example
As you can see the route has two parameters a library id and a file to be uploaded and an authentification token.
The problems appear when I try to implement an upload feature in my angular web app.Indeed when I call my $http post request a OPTIONS preflight request is sent to my server.This OPTIONS request doesn't seem to have any of the parameters given to the post request it precedes making my authentification middleware (that has the function of validating the user/token) on my server side respond with a 401 error.
More exactly:
XMLHttpRequest cannot load ..... Response for preflight has invalid http status code 401
It seems that those preflight request are made by the browser when say detect a cross origin resource sharing. This is were I hit a brick wall. I cannot seem to understand how to:
- either send the token with the options request to validate this request
- or to bypass this options request to directly send my post request.
my upload function looks like this:
$http({
method: 'POST',
url: 'my-upload-url',
headers: {
'Content-Type': 'multipart/form-data'
},
data: {
library: my-library-id,
file: my-file-to-upload,
token: user-authentification-token
},
transformRequest: function (data, headersGetter) {
var formData = new FormData();
angular.forEach(data, function (value, key) {
formData.append(key, value);
});
var headers = headersGetter();
delete headers['Content-Type'];
return formData;
}
})
.success(function (data) {
})
.error(function (data, status) {
});
My questions are:
Is there a way to actually send my user token in the OPTIONS request to make it valid server side?
Is there a way of formatting my post request (header/data/params) to make it bypass this preflight browser request?
Is the formatting of my post request wrong in any way making it trigger the OPTIONS request from Chrome?
Thank you in advance for your help.
Martin
In cors, the OPTIONS method is used to tell the server what will your request do. The server must handle the OPTIONS correctly so your main request will send normally. The browser will send the OPTIONS request automatically when your request is a complex cross origin request.
To bypass the OPTIONS request your request should be POST and GET and content-type must be application/x-www-form-urlencoded, multipart/form-data, or text/plain and the headers only contain Accept, Accept-Language and Content-Language.
Your request is not wrong. The reason is that your request is a cross origin request and it isn't a simple request.
So the best way to solve this problem is to make your server handle the cors request correctly.
For express you can use https://github.com/expressjs/cors
see more: cors-mdn cors-w3c
Related
I have created angular js app in which I have integrate twitch api , the api is
return $http({
url: "https://api.twitch.tv/kraken/streams",
method: "GET",
params: { channel: channel, limit: 1 },
headers: { "Client-Id": "XXXXXXXXXXXXXXX" }
})
the problem is when I reload the page the api is working but when my state changes without page reload I am getting cross origin error from this api.
the error is
Failed to load https://api.twitch.tv/kraken/streams?channel=eliazOne&limit=1: Request header field RefreshToken is not allowed by Access-Control-Allow-Headers in preflight response.
anyone has idea how to resolve cross error
When you make a request to a different domain this is called a cross domain request. Also known as a CORS request.
When you POST / PUT data to a different domain it will make an OPTIONS request first. This is to ensure that the server has Access-Control-Allow-Headers in place on the response. These headers should permit access to the domain you are making the request from. If these headers are not present then when the OPTIONS request is made it will fail and the POST / PUT will never be made.
See here for more info https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Preflighted_requests
The simple answer is to just add these headers to your server.
I don't know if this is something Angular does on its own by default or if it's a bug elsewhere in your code, but you or Angular is sending a RefreshToken header as part of your failing request, which is not allowed per Access-Control-Allow-Headers response header in the pre-flight OPTIONS request.
$ curl -XOPTIONS https://api.twitch.tv/kraken/streams -is | grep Headers
Access-Control-Allow-Headers: Accept, Accept-Language, Authorization, Client-Id, Twitch-Api-Token, X-Forwarded-Proto, X-Requested-With, X-Csrf-Token, Content-Type, X-Device-Id
This is something all cross-origin requests do. The browser sends an OPTIONS request to make sure the real request it's about to make is allowed by the criteria set by the cross-origin server.
You need to add a ?callback=? with the URL you are passing along with a callback function. Please follow the link How to use JQuery to Access Twitch streams
This is JQuery example but the concept is same.
$.getJSON('https://api.twitch.tv/kraken/streams/' + name + '?callback=?',
function(channel){
if (channel["stream"] == null) {
$("#all").append("<p>" + channel._links.self + "</p>");
}
else {
$("#all").append("<p>Fail</p>");
}
});
});
Angular 2 post request showing this error.
do_login(username: string,password: string): Observable<string> {
let body = JSON.stringify({"username":username,"password":password});
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this.http.post(this.loginUrl, body, options)
.map((response) => {
return response.json();
}
);
}
Get request working fine. and POST url working fine when calling on REST client
What is missing in my code. Thanks for your helps.
CORS is involved, as there is an OPTIONS request that precedes the POST request.
The server is not providing a valid response to the OPTIONS request - resulting the error you have listed above. You will need to investigate the CORS configuration of your server, as that's where to problem lies - not in the above code.
If the GET is working fine, you should look at the Access-Control-Allow-Methods configuration
It's because of CORS. You don't handle preflighted requests on the server side. This is what the 405 status code says:
405 Method Not Allowed
A request method is not supported for the requested resource; for example, a GET request on a form which requires data to be presented via POST, or a PUT request on a read-only resource.
It works for GET methods because you're in the case of simple requests, not preflighted ones. It's typically a server side issue not a frontend / Angular2 one.
These articles could help you to understand what happens:
http://restlet.com/blog/2015/12/15/understanding-and-using-cors/
http://restlet.com/blog/2016/09/27/how-to-fix-cors-problems/
I am trying to make a post request from AngularJS to WebAPI on a different domain.
$http({
method: 'POST',
url: 'http://www.test.com/api/app/controller',
data: postdata,
headers: {
'Content-Type': 'application/json'
}
})
.then(function(response) {
// Do stuff
}, function() {
// Show error
})
.finally(function() {
// Cancel loading indicator
});
I believe Web API is setup correctly to handle CORS requests. If I make a CORS OPTIONS request using Chrome Advanced REST client, the correct headers and a 200 response code are returned.
When I make the POST call above, a preflight OPTIONS request is made. This always times out with a 504 code. The logging in my Application_BeginRequest is never hit (which it is when calling from Chrome plugin).
What is the difference between calling from AngularJS and the Chrome plugin? Both are being run from the same machine and AngularJS is running in an application on localhost. The same headers are being set in both calls.
This was a stupid mistake on my behalf. I am answering the question (rather than deleting) in case somebody does the same.
I was pointing to a service containing a typo:
url: 'http://www.test2.com/api/app/controller',
Instead of:
url: 'http://www.test.com/api/app/controller',
My CORS pre-flight request was working without an issue, it was just never getting to the right server.
My code is as below
$scope.LoadSearchResults = function () {
$scope.hotels = '';
$scope.allData = '';
var mydata = '';
$http({
url: 'myurl',
Authorization : 'code',
method: 'POST',
header: { 'Content-type': 'text/json' }
})
.success(function (mydata) {
$scope.hotels = JSON.parse(JSON.stringify(mydata.SearchResponse.Hotels.Hotel));
$scope.allData = JSON.parse(JSON.stringify(mydata.SearchResponse.Hotels.Hotel));
$scope.AllLocality($scope.allData);
})
.error(function (mydata) {
$scope.hotels = "Failed";
console.log(JSON.stringify(mydata));
});
}
and getting error is
"Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://example.com/some-page. (Reason: CORS header 'Access-Control-Allow-Origin' missing)."
How could I solve this problem?
this is not an Angular problem.
the reason is you are firing a cross origin request (e.g. your app sits on a domain and the request specified by url: 'myurl' is for a different domain) and HTTP does not allow it unless permissions are specifically added by the server receiving the request by adding a CORS header to the response.
to solve your problem you please check here one of the 4 options (I am sure you can find more depending on your specific setup):
make your app fire the request to the same domain, which is normally the preferred and most secure approach (e.g. the web app hosting the angular code is also responsible of responding to the xhr you are doing via $http)
if you have control on how the server creates the response, it should be fairly easy to add such headers (I cannot add an example here as it is entirely dependent on the web server or application gateway you are using)
if you don't have control over the server response (and most importantly its headers) you can add your own server in the middle that would act as a request proxy, thus making your JS app firing the request to your server (not incurring CORS problems) and the server itself making the proxy request to the 3rd party provider
if you just need GET requests (which doesn't seem the case from your snippet) you can use angular's $http JSONP if the server supports this type of requests.
Hi I have got it same issue but now i have resolved it by using below:
header('Access-Control-Allow-Origin: *');
header("Access-Control-Allow-Headers: X-API-KEY, Origin, X-Requested-With, Content-Type, Accept, Access-Control-Request-Method, Authorization");
header("Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, DELETE");
please pass header on controller after parent::__construct();
I am trying to send a POST request to an endpoint over HTTPS.
The request has 2 headers, content-type (application/json) and an apiKey.
I am using the request in a PhoneGap application built in Angular, and when the request is sent its method is changed to OPTIONS.
I know this is standard practice for browsers due to CORS, but I have a payload which I need the server to take, and I'm told by the server guys that OPTIONS requests have an empty payload with CORS (although I can't find verification on this).
The server is set up for CORS and should accept POST and OPTIONS.
For some reason my request hangs.
Angular code:
var submitDBIDResource = $resource(env.loginUserUrl, {}, {
save: {
method: 'POST',
headers: { 'apiKey': apiKey }
}
});
submitDBIDResource.save({"dbid": dbid}).$promise.then(function(data) {
console.log(data);
return data;
});
I have in my config.xml file
Any ideas what I need to do?
Thanks
The browser will automatically send an OPTIONS request before it sends the POST request. The OPTIONS request must respond with the appropriate response or else the browser will not send the POST request.
Your backend guys need to create two request handlers, one for the OPTIONS and one for the POST.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS