Chrome NOT performing a preflight request - google-chrome

I've worked with some previous APIs in AngularJS, however there has always been a preflight using the OPTIONS method. Now I am building a new API from scratch and for some reason, AngularJS does NOT send a preflight request.
From what I have experienced AngularJS always sends a preflight when working with CORS, but not in my case. So question is really, what triggers a preflight in AngularJS if its not request to a domain which is not the same as the one AngularJS is being hosted on?
The example I have is quite easy and looks like this:
$scope.submit = function () {
$http.post('https://slimapi.devz/')
.then(function successCallback(response){
alert(response.data);
}, function errorCallback(response) {
console.log(response);
});
}
The requesting URL is https://slimfrontend.devz/.
Chrome gets triggered by the response headers in the XHR with the POST method, and will not display the result, however, the result is being fetched (as seen in timeline). But again, there is no sign of OPTIONS preflight.
Here is a picture of what my request looks like, and as you can see by the arrow. Chrome does detect the bad match of the origin.

MDN has a good description:
In particular, a request is preflighted if any of the following conditions is true:
(I paraphrase the rest below)
If the request uses any of the following methods (such as PUT)
If particular HTTP headers are set by the JS
If the Content-Type is not a valid value for the enctype attribute of an HTML <form>
IIRC, Angular defaults to sending application/json payloads (which trigger the final condition above).
Your example is of a POST request where you aren't POSTing any data (which is odd) so there is no Content-Type.

Related

Chrome dev tools fails to show response even the content returned has header Content-Type:text/html; charset=UTF-8

Why does my Chrome developer tools show
Failed to show response data
in response when the content returned is of type text/html?
What is the alternative to see the returned response in developer tools?
I think this only happens when you have 'Preserve log' checked and you are trying to view the response data of a previous request after you have navigated away.
For example, I viewed the Response to loading this Stack Overflow question. You can see it.
The second time, I reloaded this page but didn't look at the Headers or Response. I navigated to a different website. Now when I look at the response, it shows 'Failed to load response data'.
This is a known issue, that's been around for a while, and debated a lot.
As described by Gideon, this is a known issue with Chrome that has been open for more than 5 years with no apparent interest in fixing it.
Unfortunately, in my case, the window.onunload = function() { debugger; } workaround didn't work either. So far the best workaround I've found is to use Firefox, which does display response data even after a navigation. The Firefox devtools also have a lot of nice features missing in Chrome, such as syntax highlighting the response data if it is html and automatically parsing it if it is JSON.
For the ones who are getting the error while requesting JSON data:
If your are requesting JSON data, the JSON might be too large and that what cause the error to happen.
My solution is to copy the request link to new tab (get request from browser)
copy the data to JSON viewer online where you have auto parsing and work on it there.
As described by Gideon, this is a known issue.
For use window.onunload = function() { debugger; } instead.
But you can add a breakpoint in Source tab, then can solve your problem.
like this:
If you make an AJAX request with fetch, the response isn't shown unless it's read with .text(), .json(), etc.
If you just do:
r = fetch("/some-path");
the response won't be shown in dev tools.
It shows up after you run:
r.then(r => r.text())
"Failed to show response data" can also happen if you are doing crossdomain requests and the remote host is not properly handling the CORS headers. Check your js console for errors.
For the once who receive this error while requesting large JSON data it is, as mentioned by Blauhirn, not a solution to just open the request in new tab if you are using authentication headers and suchlike.
Forturnatly chrome does have other options such as Copy -> Copy as curl.
Running this call from the commandoline through cURL will be a exact replicate of the original call.
I added > ~/result.json to the last part of the commando to save the result to a file.
Otherwise it will be outputted to the console.
For those coming here from Google, and for whom the previous answers do not solve the mystery...
If you use XHR to make a server call, but do not return a response, this error will occur.
Example (from Nodejs/React but could equally be js/php):
App.tsx
const handleClickEvent = () => {
fetch('/routeInAppjs?someVar=someValue&nutherVar=summat_else', {
method: 'GET',
mode: 'same-origin',
credentials: 'include',
headers: {
'content-type': 'application/json',
dataType: 'json',
},
}).then((response) => {
console.log(response)
});
}
App.js
app.route('/getAllPublicDatasheets').get(async function (req, res) {
const { someVar, nutherVar } = req.query;
console.log('Ending here without a return...')
});
Console.log will here report:
Failed to show response data
To fix, add the return response to bottom of your route (server-side):
res.json('Adding this below the console.log in App.js route will solve it.');
I had the same problem and none of the answers worked, finally i noticed i had made a huge mistake and had chosen other as you can see
Now this seems like a dumb mistake but the thing is even after removing and reinstalling chrome the problem had remained (settings are not uninstalled by default when removing chrome) and so it took me a while until I found this and choose All again...!
This happened because my backend doesn't handle OPTIONS method and because I had clicked on other by mistake which caused me to spend a couple days trying answers!
As long as the body of the Response is not consumed within your code (using .json() or .text() for instance), it won't be displayed in the preview tab of Chrome dev tools
Bug still active.
This happens when JS becomes the initiator for new page(200), or redirect(301/302)
1 possible way to fix it - it disable JavaScript on request.
I.e. in puppeteer you can use: page.setJavaScriptEnabled(false) while intercepting request(page.on('request'))
another possibility is that the server does not handle the OPTIONS request.
One workaround is to use Postman with same request url, headers and payload.
It will give response for sure.
For me, the issue happens when the returned JSON file is too large.
If you just want to see the response, you can get it with the help of Postman. See the steps below:
Copy the request with all information(including URL, header, token, etc) from chrome debugger through Chrome Developer Tools->Network Tab->find the request->right click on it->Copy->Copy as cURL.
Open postman, import->Rawtext, paste the content. Postman will recreate the same request. Then run the request you should see the JSON response.
[Import cURL in postmain][1]: https://i.stack.imgur.com/dL9Qo.png
If you want to reduce the size of the API response, maybe you can return fewer fields in the response. For mongoose, you can easily do this by providing a field name list when calling the find() method.
For exmaple, convert the method from:
const users = await User.find().lean();
To:
const users = await User.find({}, '_id username email role timecreated').lean();
In my case, there is field called description, which is a large string. After removing it from the field list, the response size is reduced from 6.6 MB to 404 KB.
Use firefox, it always display the response and give the same tools that chrome does.

HTTP "DELETE" request hangs on Elastic Beanstalk

I deployed to AWS for first time, and experienced a very strange behavior. I use AngularJS and there is a function that performs $http service call with DELETE method specified.
var fn = function () {
$http({ method: "DELETE", url: "/active/route/"})
...
and when I perform it, request hangs for a while and get refused.
I've changed fn to
function () {
$http({ method: "POST", url: "/active/route/delete"})
...
And it worked just fine.
I want to know if beanstalk has specific policy about some HTTP methods or what was causing this behavior?
I know this has been fairly inactive for a while but I figured out what was happening in my case and thought I would post it here for anyone else who needs it. Basically http DELETE methods do not play nice if you are sending a request that contains a request body. Pure RESTful services should be employing the URI to pass object ids back and form, rather than form data or in the request body. When the data is passed via request body some web servers will read that as a POST method, which can wreak havoc on your API.
In my case I was passing the object id both in the URI and, as a side effect of my architecture, in the request body. This works perfectly ok in all cases except DELETE. To fix the issue I simply removed the unnecessary request body and viola!
Hope this helps someone.
See this post for more detail:
http://www.spenceruresk.com/2011/11/http-delete-requests-that-include-a-body/

HTTP Status 0 from AngularJS Get for JSON

I'm running a $http.get for a JSON and am getting a status of 0. I've downloaded the same JSON and the get works locally, and in Python using the requests library I can get the JSON no problem, but in AngularJS it's not working. What I don't understand is why angular isn't getting it but everything else is. Code snippet below.
function AgentListCtrl($scope, $http) {
$http.get('http://foo.bar/api/objects').success(function(data) {
$scope.objects = data;
}).error(function(data, status) {
$scope.status1 = status;
});
This provides the JSON and parses it when using a local file, but otherwise it fails and sets status1 to 0.
Just to make this clear since is not directly stated in the above answer (but in its comments) and, like me, some Angular newbies may be spending some time on this:
Angular's $resource will be able to execute a REST verb on another server, which in turn will respond correctly (with a status 200). Angular will nevertheless fail with a cryptical message, identifyiable by the status 0. It is further misleading since, in a browser's debugger, you may actually see the server's answer.
Angular will do an OPTIONS request on a cross-domain request (at least for the default query() method) unless specified on the contrary. Usually the server will not answer with the desired content (i.e. your representation). One simple way of doing this per request is specifying the method to be 'GET'.
$resource('http://yourserver/yourentity/:id', {}, {query: {method: 'GET'});
The server answering your REST requests MUST include the headers specified by CORS [1] in order to allow Angular to consume properly the response. Essentially this means including the Access-Control-Allow-Origin header in your response, specifying the servers from where the request comes from, that are allowed. This value may be *.
Complementing this answer for anyone integrating AngularJS with spring-data-rest-webmvc:
the HATEOAS json formatted response will not be properly consumed by Angular, producing instead the error Expected response to contain an array but got an object. This is solved by adding the isArray: false parameter to the $resouce's configuration;
a very to-the-point example of configuring CORS for the spring-data-rest-webmvc scenario is presented at [2] (see the SimpleCORSFilter)
[1] https://developer.mozilla.org/en/docs/HTTP/Access_control_CORS
[2] https://spring.io/guides/gs/rest-service-cors/
In your code, the status assignment only occurs when the error happens. You should be able to get the status when the call was made successfully like this:
success(function(data, status, headers, config) {
$scope.objects = data;
$scope.status1 = status;
}).error(function(data, status) {
$scope.status1 = status;
});
I was having a similar problem myself. A third party API that returns JSON just fine through every other means was failing with status 0 when called through Angular's $http.get() method.
In my case there wasn't any CORS problem. Instead, the URL I was using for the API was not quite right and the server was issuing a 301 response. Angular wasn't respecting the redirect.
Word to the wise.

CORS withCredentials XHR preflight not posting Cookies in Firefox

I'm trying to do a CORS XHR post w/ credentials. It works great in Chrome, but not in Firefox. The cookies are not present in the pre-flight request headers, and so I'm seeing a 302. This works perfectly in Chrome, as cookies are in the pre-flight request headers and the subsequent POST goes through.
Why wouldn't this work in FF? What am I missing?
// assume url, boundEventHandler and uploadData are defined, as this definitely works in Chrome
var xhr = new XMLHttpRequest();
xhr.open("POST", url, true);
xhr.addEventListener ("readystatechange", boundEventHandler, false);
xhr.withCredentials = true; // FWIW, I've also tried the string 'true'
xhr.send(uploadData);
Any ideas? I see some posts that say I can proxy the request on the server side, but I'd prefer to get this working in accordance w/ the CORS spec.
Thanks!
Per spec at https://www.w3.org/TR/cors/#resource-preflight-requests the preflight request never includes cookies. Specifically, the spec says:
Exclude user credentials.
and that links to https://www.w3.org/TR/cors/#user-credentials which says:
The term user credentials for the purposes of this specification means
cookies, HTTP authentication, and client-side SSL (...).
That said, the code snippet you quote above shouldn't involve a preflight at all: there are no upload event listeners, the method is as simple method, and there are no author headers set. So if you're really seeing a preflight request, the first question is why that's happening. Do you have any extensions in Firefox that might be munging your XMLHttpRequest object?
Now Chromium(the 4th of July 2014) doesn't sent cookie with a preflight request.
https://code.google.com/p/chromium/issues/detail?id=377541

How can I access auth-only Twitter API methods from a web application

I have a web application for iPhone, which will ultimately run within a PhoneGap application - but for now I'm running it in Safari.
The application needs to access tweets from Twitter friends, including private tweets. So I've implemented OAuth using the Scribe library. I successfully bounce users to Twitter, have them authenticate, then bounce back.
At this point the web app has oAuth credentials (key and token) which it persists locally. From here on I'd like it to user the Twitter statuses/user_timeline.json method to grab tweets for a particular user. I have the application using JSONP requests to do this with unprotected tweets successfully; when it accesses the timeline of a private Twitter feed, an HTTP basic authentication dialog appears in the app.
I believe that I need to provide the OAuth credentials to Twitter, so that my web application can identify and authenticate itself. Twitter recommends doing so through the addition of an HTTP Authorization header, but as I'm using JSONP for the request I don't think this is an option for me. Am I right in assuming this?
My options therefore appear to either be putting the oAuth credentials as query-string parameters (which Twitter recommends against, but documentation suggests still supports); or proxying all the Tweets through an intermediate server. I'd rather avoid the latter.
I access the Twitter API using URLs of the form
http://api.twitter.com/1/statuses/user_timeline.json?user_id=29191439&oauth_nonce=XXXXXXXXXXX&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1272323042&oauth_consumer_key=XXXXXXXXXX&oauth_signature=XXXXXXXXXX&oauth_version=1.0
When user_id is a public user, this works fine. When user_id is a private user, I get that HTTP Basic Auth dialog. Any idea what I'm doing wrong? I'm hoping it's something embarrassingly simple like "forgetting an important parameter"...
The oAuth stanza needs to be exact, as per http://dev.twitter.com/pages/auth#auth-request - I ended up building an Authorization: header that I could first check with curl.
I built it using the really helpful interactive request checker at http://hueniverse.com/2008/10/beginners-guide-to-oauth-part-iv-signing-requests/
Here's a friends API request for a protected user:
curl -v -H 'Authorization: OAuth realm="https://api.twitter.com/1/friends/ids.json", oauth_consumer_key="XXXXXXXXXXXXXXXX", oauth_token="XXXXXXXXXXXXXXXX", oauth_nonce="XXXXXXXXXXXXXXXX", oauth_timestamp="1300728665", oauth_signature_method="HMAC-SHA1", oauth_version="1.0", oauth_signature="XXXXXXXXXXXXXXXX%3D"' https://api.twitter.com/1/friends/ids.json?user_id=254723679
It's worth re-iterating that as you've tried to do, instead of setting the Authorization header via e.g. jquery's beforeSend function, that for cross-domain JSONP requests (which can't add HTTP headers) you can make oAuth requests by putting all the relevant key/value pairs in the GET request. This should hopefully help out various other questioners, e.g
Set Headers with jQuery.ajax and JSONP?
Modify HTTP Headers for a JSONP request
Using only JQuery to update Twitter (OAuth)
Your request looks like it has a couple of problems; it's missing the user's oauth_token plus the oauth_signature doesn't look like it has been base64 encoded (because it's missing a hex encoded = or ==, %3 or %3D%3D respectively).
Here's my GET equivalent using oAuth encoded querystring params, which you can use in a cross-domain JSONP call:
https://api.twitter.com/1/friends/ids.json?user_id=254723679&realm=https://api.twitter.com/1/friends/ids.json&oauth_consumer_key=XXXXXXXXXXXXXXXX&oauth_token=XXXXXXXXXXXXXXXX&oauth_nonce=XXXXXXXXXXXXXXXX&oauth_timestamp=1300728665&oauth_signature_method=HMAC-SHA1&oauth_version=1.0&oauth_signature=XXXXXXXXXXXXXXXX%3D
I was struggling with similar problem of making JSONP requests from Jquery, the above answer helped just to add what I did to achieve my solution.
I am doing server to server oauth and then I send oauth token, secret, consumer key and secret (this is temporary solution by the time we put a proxy to protect consumer secret). You can replace this to token acquiring code at client.
Oauth.js and Sha1.js download link!
Once signature is generated.
Now there are 2 problems:
JSONP header cannot be edited
Signed arguments which needs to be sent as part of oauth have problem with callback=? (a regular way of using JSONP).
As above answer says 1 cannot be done.
Also, callback=? won't work as the parameter list has to be signed and while sending the request to remote server Jquery replace callback=? to some name like callback=Jquery1232453234. So a named handler has to be used.
function my_twitter_resp_handler(data){
console.log(JSON.stringify(data));
}
and getJSON did not work with named function handler, so I used
var accessor = {
consumerSecret: XXXXXXXXXXXXXXXXXXXXXX,
tokenSecret : XXXXXXXXXXXXXXXXXXXXXX
};
var message = { action: "https://api.twitter.com/1/statuses/home_timeline.json",
method: "GET",
parameters: []
};
message.parameters.push(['realm', "https://api.twitter.com/1/statuses/home_timeline.json"]);
message.parameters.push(['oauth_version', '1.0']);
message.parameters.push(['oauth_signature_method', 'HMAC-SHA1']);
message.parameters.push(['oauth_consumer_key', XXXXXXXXXXXXXXXX]);
message.parameters.push(['oauth_token', XXXXXXXXXXXXXXX]);
message.parameters.push(['callback', 'my_twitter_resp_handler']);
OAuth.completeRequest(message, accessor);
var parameterMap = OAuth.getParameterMap(message.parameters);
Create url with base url and key value pairs from parameterMap
jQuery.ajax({
url: url,
dataType: "jsonp",
type: "GET",
});