reduxjs / redux

A JS library for predictable global state management
https://redux.js.org
MIT License
60.8k stars 15.27k forks source link

Issues with fetch() in Reddit docs example in some browsers #615

Closed bmueller-sykes closed 8 years ago

bmueller-sykes commented 9 years ago

Hi,

Apologies up front if this is a) not the right spot for this, or b) really obvious and I've missed it. I'm working through the Redux docs, and am trying to get the Reddit example working. In this function:

function fetchPosts(reddit) {
    return dispatch => {
    dispatch(requestPosts(reddit));
    return fetch(`http://www.reddit.com/r/${reddit}.json`)
      .then(req => req.json())
      .then(json => dispatch(receivePosts(reddit, json)))
    }
}

...I see a network error when I execute the code in Safari and IE9 (presumably IE8 as well). It works in Chrome, Firefox, and IE10+. Specifically, it says "unhanded promise rejection", and then the error details say "network request failed". My guess is this has to do with a polyfill not being invoked where it should be, but I don't know ES6/Redux/etc well enough to be able to suss out exactly where the error is.

Help?

Thanks!

danmaz74 commented 9 years ago

You can try using

https://www.npmjs.com/package/es6-promise https://www.npmjs.com/package/whatwg-fetch

require('es6-promise').polyfill(); require("whatwg-fetch");

bmueller-sykes commented 9 years ago

Thx for the reply. I tried what you suggested, but to no avail in Safari. In fact, the first line of code in the sample app is:

import babel-core/polyfill

...which, if I'm reading my docs correctly, should have provided the polyfill stuff I needed in the first place. I also stumbled on this post:

http://mts.io/2015/04/08/webpack-shims-polyfills/

...which has fetch being invoked as a Webpack plugin. I've tried that, I tried your example, and the basic example in the Redux docs. It kind of looks like something else is going on. Here's a screenshot of what I'm seeing in Safari's console (I'm running 8.0.8 on El Cap):

screen shot 2015-08-24 at 5 01 42 pm :

Anybody else have this behavior, or is this unique to me?

Thanks again!

danmaz74 commented 9 years ago

Sorry, I wasn't clear in my answer - what I meant was to try using those two plugins instead of the babel-core polyfills, not together with them, and see if that helped.

Regarding the article you linked, only loading the fetch polyfill without es6-promise isn't going to work in browsers that don't already implement javascript Promise.

http://caniuse.com/#feat=promises http://caniuse.com/#feat=fetch

My suggestion is to create a very simple test script, without Redux and everything else, which just tests the polyfill and tries to fetch() something. When you have that working, try to use the same approach in the example.

One last comment: instead of using fetch you can use $.ajax if you already use jquery.

EDIT: one more comment. The example doesn't handle connection errors, but I'm sure @gaearon simply wanted to keep it simple. If somehow what you have are connection errors, you can try using this code: https://github.com/github/fetch#handling-http-error-statuses

gaearon commented 9 years ago

EDIT: one more comment. The example doesn't handle connection errors, but I'm sure @gaearon simply wanted to keep it simple.

“Real world” example handles errors. “Async” doesn't for simplicity.

gaearon commented 9 years ago

Sorry, I'm not sure I understand the issue.

Both Async Actions document, Reddit API example source code in docs and the actual example in examples/async contain this line:

import fetch from 'isomorphic-fetch';

That's the polyfill we suggest you to use. It uses whatwg-fetch on the client, but also works on server.

gaearon commented 9 years ago

Hope this helps: https://github.com/rackt/redux/commit/ad6a6355144d80ec6336e53c922fdee8192b1ef6.

bmueller-sykes commented 9 years ago

Sorry to pester about this, but I must be missing something. @gaearon, I took your Reddit example verbatim from the docs, and ran that code on top of webpack. It works as advertised in Chrome, Firefox, and IE10+ when running inside the webpack dev server (localhost:3000), and even when I publish the project. I see logging outputted to the console, and I see posts pulled from Reddit, etc.

Switching over to Safari (and IE9, on the published version) gives me the errors you see above in this thread, the rather unhelpful "network request failed" error. The actions.js file has this line at the top:

import fetch from 'isomorphic-fetch;

...as you suggest, the index.js file has this as the very first line:

import 'babel-core/polyfill';

...as your examples require, and my package.json file has the following dependencies (this doesn't include all the dev dependencies required to get webpack up and running):

 "dependencies": {
    "react": "0.13.3",
    "redux": "1.0.1",
    "react-redux": "0.9.0",
    "redux-logger": "1.0.3",
    "redux-thunk": "0.1.0",
    "isomorphic-fetch": "2.1.1"
}

So I think I have everything I need to get this running. When I first hit this error, I thought perhaps the import fetch from isomorphic-fetch line wasn't doing its thing, I tried the es6-promise and whatwg stuff that @danmaz74 suggested, and I also tried adding the polyfill code as a webpack plugin, but none of those things worked, so I backed out of those tests, and I'm back with just your code.

It does appear to be an issue with Safari/IE9 not having fetch support that Chrome/FF/IE10+ appear to have, but where I'm getting lost is that I'm assuming your sample code is supposed to work out of the box in all A-grade browsers (at least IE9+, recent versions of Safari, Chrome, FF). Because I cannot get it to work in those browser, I'm somewhat stumped. Am I being stupid, or have I stumbled onto a bug?

Just to be clear, are you able to get this example running in Safari and IE9? If so, did you have to do anything differently?

(By the way, this issue aside, I really like Redux, and I think you're doing a terrific job with it)

gaearon commented 9 years ago

I don't have IE9 so I can't check. I can run it in Safari fine, but I'm using Safari 9, so maybe there's a problem with earlier version.

There are two places in fetch source that raise the error you posted in screenshot:

https://github.com/github/fetch/blob/master/fetch.js#L314 https://github.com/github/fetch/blob/master/fetch.js#L328

I don't think it's a problem with polyfill—it's just XHR is failing for some reason. Can you try running vanilla XMLHttpRequest to Reddit?

bmueller-sykes commented 9 years ago

Sure, I'll try! I'll do that shortly and post results.

Meanwhile, you can grab a free VM of a whole slew of version of IE from here:

http://dev.modern.ie/tools/vms/mac/

Totally invaluable for testing. I wish I worked in a world where I could totally bail on IE8/9, but sadly too many corporate IT departments still use older browsers, so I have to make sure everything works on those platforms. Sigh.

If you're using VMWare, you should be able to access the web server on your host machine (I'm assuming you have a Mac) via:

http://172.16.226.1 ...or: http://172.16.226.1/~[username]

for VirtualBox, it's:

http://10.0.2.2 ...or: http://10.0.2.2/~[username]

I cribbed that info from here:

http://stackoverflow.com/questions/3235011/vmware-fusion-connecting-to-hosts-web-server-from-guest

gaearon commented 9 years ago

Anyway, feel free to use something else instead of fetch if it's causing you troubles: axios, superagent, etc.

bmueller-sykes commented 9 years ago

Quick update: running vanilla XHR to Reddit fails in Safari when accessing files via Apache (e.g. localhost/path/to/file), but succeeds when the file is opened as a file in Safari (e.g. file:///Users/username/path/to/file. Makes no sense. jQuery fails in the same way, so at least there's consistency. It's gotta be some sort of security thing, and seems like it should be some sort of CORS thing, but lord knows what it actually is. So it seems like it's not Redux-related at all, which is good. If I figure it out, I'll update this issue, just in case anybody else runs into the same problem.

gaearon commented 9 years ago

Oh, are you saying you're running from file://? This is a recipe for CORS issues, so you should fire up a local server instead. :-) We use webpack-dev-server in examples.

bmueller-sykes commented 9 years ago

No, sorry. In my XHR-only example (stripped of React, Redux, Webpack, etc), my XHR requests fail when I access the page via Apache (e.g. localhost/path/to/file), but succeed when I access the page via file access (e.g. file:///path/to/file). This is exactly the opposite of what I would expect, and is therefore vexing.

gaearon commented 9 years ago

Have you tried the example as is? It's in examples/async folder in the repo.

bmueller-sykes commented 9 years ago

Just did. I get terminal errors when doing npm start. Looks like path issues with webpack, I think, though I'm not sure exactly what. (I realize this is getting way off-topic. Sorry for that.)

screen shot 2015-08-25 at 3 26 03 pm
gaearon commented 9 years ago

https://github.com/rackt/redux/blob/master/docs/introduction/Examples.md#note-on-copying

gaearon commented 9 years ago

(Examples are not meant to be run directly. You should check out Redux, npm install in the root, then npm install inside the examples.)

We need to put this in their README because everybody keeps doing that. :-)

bmueller-sykes commented 9 years ago

Ah. Yeah, I completely missed that. I'd humbly suggest that you have examples that are completely standalone, since that comes closer to mimicking a real-world implementation, rather than have them rely on something in a parent directory. Or at least that feels right to me.

Anyway, I did as you suggested, and now the async example starts up. I get the identical error message in Safari (8.0.8) that I was getting otherwise (image attached below). I'm thoroughly baffled. Tomorrow, I'll have access to another machine, and I'll try this same test there. Perhaps there's something uniquely weird with my machine--though I sort of doubt it.

So, interestingly enough, in XHR-only test bed, I tried using jQuery, and got the same results. Then I tried setting "jsonp": true in the $.ajax() config, and in that case, Safari made a request to Reddit and got back JSON. My code had trouble parsing the data at that point, but that seems like a minor point. Safari was able to make the XHR request and get data back, so that suggests there's some sort of CORS-type security issue at work here. I have no guess as to why, and my only real solution at this point is to throw my computer out of the window and take up farming.

screen shot 2015-08-25 at 4 10 18 pm

gaearon commented 9 years ago

Maybe at work you're behind a proxy that restricts this kind of stuff.

bmueller-sykes commented 9 years ago

If that were true, I'd expect the same behavior in all browsers. Also, this happened from both my home (yesterday) and office (today), so I don't think a proxy is at play. For a while, I thought maybe Google's 8.8.8.8 DNS might be to blame, so I ripped that out, and nothing changed. Frustrating. Thanks for helping, though! I appreciate it!

gaearon commented 9 years ago

I'd humbly suggest that you have examples that are completely standalone, since that comes closer to mimicking a real-world implementation, rather than have them rely on something in a parent directory.

The reasoning was easier development (no need to recompile while hacking on Redux source). We'll now support both ways: https://github.com/rackt/redux/pull/626

merk commented 9 years ago

I suggest installing something like mitmproxy, setting it up as a socks proxy in your network configuration and watching what safari/ie9 send and get in response.

We did see an issue with some CORS requests breaking in one of our customers environments and from what I remember it turned out to be a transparent proxy not supporting OPTION requests. Though, I believe it affected all browsers..

bmueller-sykes commented 9 years ago

I'm actually not sure Safari is making the request at all. The console suggests that the request never even gets made. grr. frustrating. Here's a screenshot of the request/response. You can see that no request even gets made (Included below)

For what it's worth, I switched to a different Mac to just make sure my primary machine wasn't in some wonky state and I got the same problem with a different machine. Again, not Redux-related, but somewhat relevant, since it may affect others attempting to get this demo to work.

screen shot 2015-08-25 at 19 35 58
danmaz74 commented 9 years ago

@bmueller-sykes just out of curiosity at this point, why don't you try to make the same call using jQuery?

merk commented 9 years ago

I see similar behaviour even in Chrome if the OPTIONS preflight request fails in a non standard way, it is worth checking.

bmueller-sykes commented 9 years ago

@danmaz74 I tested with vanilla JS, and then with jQuery as well. Both failed in the same fashion (i.e. it appeared the request never got sent in the first place). Then I tried using the JSONP setting in jQuery ajax(), and that kind of worked. The browser made the request, Reddit kicked back JSON, and then the return processing failed because my code wasn't able to handle the not-quite-JSON string. So I'm not sure yet what to make of this. It's very odd. My next step is to deploy this code on a "real" server, to see if this could have something to do with permissions when run off of localhost.

danmaz74 commented 9 years ago

@bmueller-sykes so maybe there's something strange with Reddit's API server; jQuery is mega-tested :)

bmueller-sykes commented 9 years ago

I think that's right. I'm running a test now to confirm, because I'd like to try to figure out what header is there/not there that would cause Reddit's API to behave this way. If I can figure that out, I'll post comments here. It is the case that Reddit requires some kind of OAuth authentication if you're going to use their API a lot, so it could be that accessing without authentication is okay in some browsers, and not okay in others.

It may mean that using Reddit's API in the sample docs is a bad idea, though.

danmaz74 commented 9 years ago

If the problem only comes out in a couple of browser versions, I think it's enough to put a NB in the docs. Rewriting the sample just because of this looks overkill to me.

bmueller-sykes commented 9 years ago

@danmaz74 I would respectfully disagree. (-; Granted, it's not a Redux-specific issue, but having examples that work in all modern browsers, with no caveats, seems important. But I bow to the will of the community on that point. Maybe I can find some other suitable API to swap in...

bmueller-sykes commented 9 years ago

Okay, it does look like Reddit's API and Safari aren't playing nice. I still don't know why, though my best guess is that it has something to do with the OAuth authentication Reddit appears to sort of require. No idea why that would cause Safari to just send no header information at all, though. Here's the simple, non-Redux/React/Webpack/etc test:

//var url = 'https://qrng.anu.edu.au/API/jsonI.php?length=1&type=uint8';
var url = 'http://www.reddit.com/r/reactjs.json';

var xhr = new XMLHttpRequest();
xhr.addEventListener("progress", function (evt) { console.log('progress', url, evt); } );
xhr.addEventListener("load", function (evt) { console.log('load', url, evt); } );
xhr.addEventListener("error", function (evt) { console.log('error', url, evt); } );
xhr.addEventListener("abort", function (evt) { console.log('abort', url, evt); } );         

xhr.open ("get", url, true);
xhr.send(); 

Using the Reddit URL produces an error, with this header info: screen shot 2015-08-26 at 12 01 17 pm

...and using the other URL succeeds, with this header info: screen shot 2015-08-26 at 12 00 28 pm

So at least, the example needs to say that it won't work on Safari 8 (and presumably before), and at best the example shouldn't use the Reddit API. But at least I can put this to bed and move on. Thanks for all the replies.

danmaz74 commented 9 years ago

@bmueller-sykes just to clarify: I agree that it would be better to have an example that doesn't rise problems on any browser, but not at the cost that changing that example would require. Unless you have a volunteer for doing that at hand!

bmueller-sykes commented 9 years ago

(-; fair enough. Once I get my head better wrapped around this stuff, I can try to find time to find a better JSON API that won't cause others to tear their hair out. I wouldn't think it would take too terribly long to find such a thing.

gaearon commented 9 years ago

:+1: I don't like Reddit much anyway. The only requirement is to keep existing structure (feed of stuff, with changeable feeds) and that JSON structure is as simple as possible. Maybe we can just use Github “starred” example from real-world but without normalization/pagination/etc.

danmaz74 commented 9 years ago

:+1:

gaearon commented 8 years ago

I'm closing as inactive. If you'd like to fix the docs please send a PR to the docs.

silouanwright commented 8 years ago

Note: isomorphic-fetch will NOT work in ie8. Use something else (I found fetch-polyfill to work fine)

samvincent commented 8 years ago

Sadly, I spent quite a bit of time on this as well since I needed to work with Reddit specifically.

Got a nice clue at https://github.com/github/fetch/issues/367 where the suggestion is that the error be changed to The resource could not be loaded because the App Transport Security policy requires the use of a secure connection

Switched the reddit url to use https and it worked in Safari 9. Still not certain why Safari and Chrome differ with this exactly, since vanilla XMLHttpRequest worked with http in Safari, but hoping this helps someone who is stubborn enough to work with Safari and use fetch with Reddit.

bmueller-sykes commented 8 years ago

huh. glad somebody could make a little sense out of this. I don't think I ever thought to try HTTPS. Still weird that it would work with HTTP for some browsers and HTTPS for others.