UPDATE Mar 21
After more investigation it looks like this is also showing a Play framework bug an XHR limitation on error status:
Play 2.2.1 SimpleResult 4xx Response Body Possible via CORS XHR? That, or the RESTfulness I seek is not possible with 401 codes.
Still an issue in both spaces since a 401 gets a 0 status code in Angular $http as below. So much for RESTfulness! All I want to do is provide a response body for 401 Unauthorized. From the W3C page on 4xx: "User agents SHOULD display any included entity to the user."
UPDATE Mar 20
Further investigation points to an issue within XMLHttpRequest itself, but please let me know if I am missing something below. I am spitting out the raw XHR object inside the xhr.onreadystatechange() callback as defined inside Angular's createHttpBackend():
When I log in with a known good email and password, everything works fine:
But the same XHR object for a 400 Bad Request (wrong password) has an empty response but more importantly status=0, which from Mozilla docs denotes the request was never sent (but I am seeing my API server get the request and do a DB lookup, as Postman works as above):
What gives? How does Postman get a response but XMLHttpRequest on latest Chrome and Firefox is confused?
I am sending a POST request using $http and getting conflicting responses in the browser (Chrome and FF), and Postman. Postman works fine and has the body, but it looks like angular is "cleaning" up some response codes to handle weirdness on Android browsers when templates are cached, but I can't pinpoint the spot in $httpBackend where the raw XHR is happening to intercept the response body--maybe that's because the XHR is blocking/canceling the response? Because security? I do not have any interceptors, either.
The "happy path" login works fine and I get matching 200 OK responses in the console/firebug, but 400 Bad Request doesn't have a response body.
CORS is configured on nginx (see http://enable-cors.org/server_nginx.html) and since the happy login success works the setup is functional.
My call:
var onLoginSuccess = function(data, status, headers, config){
console.log('[onLoginSuccess]')
console.log(data)
console.log(status)
console.log(headers)
console.log(config)
}
var onLoginError = function(data, status, headers, config){
console.log('[onLoginError]')
console.log(data)
console.log(status)
console.log(headers)
console.log(config)
}
var loginObj = {
'username': $scope.username,
'password': $scope.password,
'grant_type': 'password',
'client_id': 'web'
}
var doLogin = $http({
method: 'POST',
url: 'https://api/v1/token',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
transformRequest: function(obj) {
var str = [];
for(var p in obj) {
str.push(encodeURIComponent(p) + '=' + encodeURIComponent(obj[p]));
}
return str.join('&');
},
data: loginObj,
responseType: 'json'
})
doLogin.then(
onLoginSuccess,
onLoginError
)
Angular is modifying the error code so the browser and angular do not agree:
When I send the same request via Postman, the 400 Bad Request code is the same, but the body is also a json string, as expected.
Why is this not available via $http CORS inside the onLoginError() callback?
The problem I think is you are sending the POST body as JSON, rather it need to send as query param. Try this change
var loginObj = $.param({
'username': $scope.username,
'password': $scope.password,
'grant_type': 'password',
'client_id': 'web'
});
This will make your body as query params. Also don't forget to include jquery.js library
The issue ended up being with my api server code. My app was only sending Content-Type headers but I thought nginx would be able to serve up Allow-Origin headers and keep the app ignorant of any headers.
See Play 2.2.1 SimpleResult 4xx Response Body Possible via CORS XHR? for the answer. It's the Play Scala .withHeaders() part that fixed the issue via CORS.
Wonder if there's a way to have nginx handle all headers, maybe my proxy_pass section is not done right.
Related
I'm trying to do a post request to an external server using the Request - Simplified HTTP client in node.js. I expect to get the departure times of a specific bus station. But everytime I get a bad response from the server (status code 500). The code I'm using is listed below:
var request = require("request");
var options = { method: 'POST',
url: 'http://www.wsw-mobil.de/app-panel.php',
qs: { p: 'wuppertal', s: 'Blankstrasse' },
headers:
{ 'Postman-Token': 'd67fca9f-1296-1aa6-0aef-0451a16d6033',
'Cache-Control': 'no-cache',
'Content-Type': 'application/x-www-form-urlencoded' } };
request(options, function (error, response, body) {
if (error) throw new Error(error);
console.log(body);
});
As you can see I'm using the code generated by Postman.
The weird thing now: If I'm doing the post request in Postman straight away I'm getting a successful response (200). You can see all the options and the successful response in the image below:
POST request in Postman
I'm trying to get my head around this problem for several hours. Does anyone have a glue why I'm getting a bad response (500) in node.js using the request module?
I ran the code and get the following error:
Error: Cannot find module 'request'
To me, it seems like you need to add a package.json file in the same dir as you have your code and then npm install request --save this should then bring in that module and allow you to send the request.
UPDATE:
Having installed the module - I see the error coming back from the POST request.
After some trail and error the following combination of headers, seem to return a 200 OK and a list of results.
headers: { 'accept': '*/*',
'user-agent':'*' }
Not too sure what's happening with that endpoint to cause the original issue.
What is the reason the server is returning object as 'undefined' and 'XMLHttpRequest cannot load the "URL" Response for preflight is invalid (redirect).
Flow of app - its just a normal post service sending document details to the server in return should return an object holding various parameters, but its returning 'undefined'
The service for posting the document
fileUpload: {
method: 'POST',
url: config.apiPath + 'employee/service/pushRecords', //this is the URL that should return an object with different set of parameters (currently its returning Error error [undefined])
isArray: false,
params: {},
headers: {
'content-type': undefined
}
},
above service i have used after creating formdata w.r.t document
function registerFormdata(files, fieldName) {
files = files || [];
fieldName = fieldName || 'FileSent';
var returnData = new FormData();
_.each(files, function (file, ind) {
returnData.append(fieldName,file);
});
return returnData;
}
now this is the controller where these services are used
function sendFilesToServer() {
var formData = employeePushService.registerFormdata(directive.dropZoneFile.fileToUpload);
return docUploadService.fileUpload(formData)
.then(function(document) {
// Extra actions but here the server should be returning an object with set of parameters but in browser console its Error [undefined]
}).catch(logger.error);
}
Assuming that the URL target in yout post is correct, it seems that you have a CORS problem, let me explain some things.
I don't know if the server side API it's developed by yourself, if it is, you need to add the CORS access, your server must return this header:
Access-Control-Allow-Origin: http://foo.example
You can replace http://foo.example by *, it means that all request origin will have access.
First, you need to know that when in the client you make an AJAX CORS request, your browser first do a request to the server to check if the server allow the request, this request is a OPTION method, you can see this if, for example in chrome, you enable the dev tools, there, in the network tab you can see that request.
So, in that OPTIONS request, the server must set in the response headers, the Access-Control-Allow-Origin header.
So, you must check this steps, your problem is that the server side is not allowing your request.
By the way, not all the content-type are supported in CORS request, here you have more information that sure will be helpfull.
Another link to be helpfull for the problem when a 302 happens due to a redirect. In that case, the POST response must also include the Access-Control-Allow-Origin header.
I am working on an internal web application at work. In IE10 the requests work fine, but in Chrome all the AJAX requests (which there are many) are sent using OPTIONS instead of whatever defined method I give it. Technically my requests are "cross domain." The site is served on localhost:6120 and the service I'm making AJAX requests to is on 57124. This closed jquery bug defines the issue, but not a real fix.
What can I do to use the proper http method in ajax requests?
Edit:
This is in the document load of every page:
jQuery.support.cors = true;
And every AJAX is built similarly:
var url = 'http://localhost:57124/My/Rest/Call';
$.ajax({
url: url,
dataType: "json",
data: json,
async: true,
cache: false,
timeout: 30000,
headers: { "x-li-format": "json", "X-UserName": userName },
success: function (data) {
// my success stuff
},
error: function (request, status, error) {
// my error stuff
},
type: "POST"
});
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 why the jQuery bug was closed as won't-fix. 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)
Based on the fact that the request isn't sent on the default port 80/443 this Ajax call is automatically considered a cross-origin resource (CORS) request, which in other words means that the request automatically issues an OPTIONS request which checks for CORS headers on the server's/servlet's side.
This happens even if you set
crossOrigin: false;
or even if you ommit it.
The reason is simply that localhost != localhost:57124. Try sending it only to localhost without the port - it will fail, because the requested target won't be reachable, however notice that if the domain names are equal the request is sent without the OPTIONS request before POST.
I agree with Kevin B, the bug report says it all. It sounds like you are trying to make cross-domain ajax calls. If you're not familiar with the same origin policy you can start here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Same_origin_policy_for_JavaScript.
If this is not intended to be a cross-domain ajax call, try making your target url relative and see if the problem goes away. If you're really desperate look into the JSONP, but beware, mayhem lurks. There really isn't much more we can do to help you.
If it is possible pass the params through regular GET/POST with a different name and let your server side code handles it.
I had a similar issue with my own proxy to bypass CORS and I got the same error of POST->OPTION in Chrome. It was the Authorization header in my case ("x-li-format" and "X-UserName" here in your case.) I ended up passing it in a dummy format (e.g. AuthorizatinJack in GET) and I changed the code for my proxy to turn that into a header when making the call to the destination. Here it is in PHP:
if (isset($_GET['AuthorizationJack'])) {
$request_headers[] = "Authorization: Basic ".$_GET['AuthorizationJack'];
}
In my case I'm calling an API hosted by AWS (API Gateway). The error happened when I tried to call the API from a domain other than the API own domain. Since I'm the API owner I enabled CORS for the test environment, as described in the Amazon Documentation.
In production this error will not happen, since the request and the api will be in the same domain.
I hope it helps!
As answered by #Dark Falcon, I simply dealt with it.
In my case, I am using node.js server, and creating a session if it does not exist. Since the OPTIONS method does not have the session details in it, it ended up creating a new session for every POST method request.
So in my app routine to create-session-if-not-exist, I just added a check to see if method is OPTIONS, and if so, just skip session creating part:
app.use(function(req, res, next) {
if (req.method !== "OPTIONS") {
if (req.session && req.session.id) {
// Session exists
next();
}else{
// Create session
next();
}
} else {
// If request method is OPTIONS, just skip this part and move to the next method.
next();
}
}
"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
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
Consider using axios
axios.get( url,
{ headers: {"Content-Type": "application/json"} } ).then( res => {
if(res.data.error) {
} else {
doAnything( res.data )
}
}).catch(function (error) {
doAnythingError(error)
});
I had this issue using fetch and axios worked perfectly.
I've encountered a very similar issue. I spent almost half a day to understand why everything works correctly in Firefox and fails in Chrome. In my case it was because of duplicated (or maybe mistyped) fields in my request header.
Use fetch instead of XHR,then the request will not be prelighted even it's cross-domained.
$.ajax({
url: '###',
contentType: 'text/plain; charset=utf-8',
async: false,
xhrFields: {
withCredentials: true,
crossDomain: true,
Authorization: "Bearer ...."
},
method: 'POST',
data: JSON.stringify( request ),
success: function (data) {
console.log(data);
}
});
the contentType: 'text/plain; charset=utf-8', or just contentType: 'text/plain', works for me!
regards!!
having a problem with getting data back from database. I am trying my best to explain the problem.
1.If I leave "mode":"no-cors" inside the code below, then I can get data back from server with Postman, but not with from my own server. Thinking it has to be my client side error
When I remove "mode":"no-cors" then I am getting 2 errors:
-Fetch API cannot load http://localhost:3000/. Request header field access-control-allow-origin is not allowed by Access-Control-Allow-Headers in preflight response.
-Uncaught (in promise) TypeError: Failed to fetch
Quick Browsing suggested to put in the "mode":"no-cors" which fixed this error, but it does not feel right thing to do.
So I thought maybe somebody has a suggestion how to approach this problem.
Really hope I was clear enough, but pretty sure I am not giving clear explanation here :S
function send(){
var myVar = {"id" : 1};
console.log("tuleb siia", document.getElementById('saada').value);
fetch("http://localhost:3000", {
method: "POST",
headers: {
"Access-Control-Allow-Origin": "*",
"Content-Type": "text/plain"
},//"mode" : "no-cors",
body: JSON.stringify(myVar)
//body: {"id" : document.getElementById('saada').value}
}).then(function(muutuja){
document.getElementById('väljund').innerHTML = JSON.stringify(muutuja);
});
}
Adding mode:'no-cors' to the request header guarantees that no response will be available in the response
Adding a "non standard" header, line 'access-control-allow-origin' will trigger a OPTIONS preflight request, which your server must handle correctly in order for the POST request to even be sent
You're also doing fetch wrong ... fetch returns a "promise" for a Response object which has promise creators for json, text, etc. depending on the content type...
In short, if your server side handles CORS correctly (which from your comment suggests it does) the following should work
function send(){
var myVar = {"id" : 1};
console.log("tuleb siia", document.getElementById('saada').value);
fetch("http://localhost:3000", {
method: "POST",
headers: {
"Content-Type": "text/plain"
},
body: JSON.stringify(myVar)
}).then(function(response) {
return response.json();
}).then(function(muutuja){
document.getElementById('väljund').innerHTML = JSON.stringify(muutuja);
});
}
however, since your code isn't really interested in JSON (it stringifies the object after all) - it's simpler to do
function send(){
var myVar = {"id" : 1};
console.log("tuleb siia", document.getElementById('saada').value);
fetch("http://localhost:3000", {
method: "POST",
headers: {
"Content-Type": "text/plain"
},
body: JSON.stringify(myVar)
}).then(function(response) {
return response.text();
}).then(function(muutuja){
document.getElementById('väljund').innerHTML = muutuja;
});
}
In my case, the problem was the protocol. I was trying to call a script url with http instead of https.
try this
await fetch(url, {
mode: 'no-cors'
})
See mozilla.org's write-up on how CORS works.
You'll need your server to send back the proper response headers, something like:
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, PUT, GET, OPTIONS
Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Authorization
Bear in mind you can use "*" for Access-Control-Allow-Origin that will only work if you're trying to pass Authentication data. In that case, you need to explicitly list the origin domains you want to allow. To allow multiple domains, see this post
you can use solutions without adding "Access-Control-Allow-Origin": "*", if your server is already using Proxy gateway this issue will not happen because the front and backend will be route in the same IP and port in client side but for development, you need one of this three solution if you don't need extra code
1- simulate the real environment by using a proxy server and configure the front and backend in the same port
2- if you using Chrome you can use the extension called Allow-Control-Allow-Origin: * it will help you to avoid this problem
3- you can use the code but some browsers versions may not support that so try to use one of the previous solutions
the best solution is using a proxy like ngnix its easy to configure and it will simulate the real situation of the production deployment
Sometimes, please check your port number. If localhost port number is mismatch, you will get the same error as well.
I was getting this error and realized my server.js wasn't running.
I'm writing a website with AngularJS which communicates with an API on the server and provides some Info.
for Log in part I should send a http post request containing Email, Password and etc. It works fine on google Chrome and IE. I mean it sends the post request and gets a token. But in FireFox as I checked in Network, It sends an OPTION request and gets 200 but after that it does not send any post! hence my login would not disappear and I wont get any token.
what should I do for this situation?
App.config :
$httpProvider.defaults.withCredentials = true;
$httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8;';
$httpProvider.interceptors.push('httpRequestInterceptor');
Function in service which sends request :
this.loginEmail = function(f_email, f_pass, deviceModel, deviceOs) {
var data = $.param({
email: f_email,
password: f_pass,
device_model: deviceModel,
device_os: deviceOs
});
return $http({
method: "POST",
url: app.baseUrl + 'login_email/' + app.storeID + '/' + app.device_id,
data: data
}).success(function(response){
return response.status;
});
/*return $http.post(app.baseUrl + 'login_email/' + app.storeID + '/' + app.device_id, data).success(function(response){
return response.status;
}).error(function(response){
return response.status;
});*/
};
Server Credentials are true
CORS seems fine because I can do get request
EDIT:
Here's another thing that may be related to this problem:
in Chrome when I get logged in for get requests it sends the Token header
but for Post it doesn't
httpRequestInterceptor :
app.factory('httpRequestInterceptor', function ($cookieStore) {
return {
request: function (config) {
config.headers['Authorization'] = $cookieStore.get('Auth-Key');;
config.headers['Accept'] = 'application/json;odata=verbose';
return config;
}
};
});
The problem was caused by apache configurations.
before:
Access-Control-Allow-Headers: "authorization"
after:
Access-Control-Allow-Headers: "authorization, Content-type"
UPDATE :
On CORS requests if API requires some special headers like Auhtorization Token you must return all OPTIONS requests 200(ok!) if not the solution above would not work anyway.
Here's the code:
if($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
header( "HTTP/1.1 200 OK" );
exit();
}
UPDATE 2 :
This OPTIONS problem occurs in REST framework for Django! For OPTIONS it evaluates the request by pursing whole api if there was a problem in it, you'll get error even though you have required permissions for sending request!
Example:
Suppose that there's a url like api/profile which needs an Authorization header for responsing profile details. You want to send the Cross Domain request for getting it. You set the right headers and click! You'll get unauthorized error! Why? Because the pre flighted request(OPTIONS) does not include any special header and browser sends it to server, server with REST framework evaluates the OPTIONS request by checking the whole request(get request with authorization header) but OPTIONS doesn't have any authorization header so this request is unauthorized!
DEVELOPMENTAL SOLUTION :
This problem can be solved either by Client-Side or Back-End. Front-End developer can install following plugin on chrome:
Allow-Control-Allow-Origin: *
Back-End developer can install a package which enables CORS on Django Framework.