admin管理员组

文章数量:1291004

I'm using superagent to upload files from my React Native app. It works perfectly fine on iOS, but on Android it gives this error:

Possible causes: the network is offline, Origin is not allowed by Access-Control-Allow-Origin, the page is being unloaded, etc.

I've created a minimal example here /@twohill/upload-example and copied the code below in case the snack goes away:

import React, { Component } from 'react';
import { Text, View, StyleSheet, TouchableOpacity } from 'react-native';
import Constants from 'expo-constants';
import * as DocumentPicker from 'expo-document-picker';
import superagent from 'superagent';

import AssetExample from './ponents/AssetExample';
import { Card } from 'react-native-paper';

const upload = (file, setMessage) => {
  const { name } = file;

  superagent.post('') // <-- change this URL to your server that accepts uploads and is CORS enabled
    .set('Authorization', 'BEARER xxxyyy') // <-- JWT token here
    .attach('uri', file, name)
    .then(
      result => setMessage(JSON.stringify({result})),
      error => setMessage(JSON.stringify({error}))
      );
};

const pickDocuments = async (setMessage) => {
  const file = await DocumentPicker.getDocumentAsync({ copyToCacheDirectory: true });
  if (file.type === "success") {
    upload(file, setMessage);
  }
}

export default class App extends Component {
  state = {
    message: null,
  }
  render() {
    const { message } = this.state;
    return (

      <View style={styles.container}>
       <TouchableOpacity onPress={() => pickDocuments(message => this.setState({message}))}>
        <Text style={styles.paragraph}>
          Tap to upload a file
        </Text>
        <Card>
          <AssetExample />
        </Card>
        <Card><Text>{message}</Text></Card>
         </TouchableOpacity>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    paddingTop: Constants.statusBarHeight,
    backgroundColor: '#ecf0f1',
    padding: 8,
  },
  paragraph: {
    margin: 24,
    fontSize: 18,
    fontWeight: 'bold',
    textAlign: 'center',
  },
});

If I console.log the error it gives the following:

Request has been terminated
Possible causes: the network is offline, Origin is not allowed by Access-Control-Allow-Origin, the page is being unloaded, etc.
* http://192.168.1.3:19001/node_modules%5Cexpo%5CAppEntry.bundle?platform=android&dev=true&minify=false&hot=false:282311:24 in crossDomainError
- node_modules\@sentry\utils\dist\instrument.js:224:24 in <anonymous>
- node_modules\event-target-shim\dist\event-target-shim.js:818:39 in EventTarget.prototype.dispatchEvent
- node_modules\react-native\Libraries\Network\XMLHttpRequest.js:566:23 in setReadyState
- node_modules\react-native\Libraries\Network\XMLHttpRequest.js:388:25 in __didCompleteResponse
- node_modules\react-native\Libraries\vendor\emitter\EventEmitter.js:190:12 in emit
- node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:436:47 in __callFunction
- node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:111:26 in __guard$argument_0
- node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:384:10 in __guard
- node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:110:17 in __guard$argument_0
* [native code]:null in callFunctionReturnFlushedQueue

As far as I can tell, on Android the app never tries an upload. My server runs express and has the cors middleware enabled with the default configuration

{
  "origin": "*",
  "methods": "GET,HEAD,PUT,PATCH,POST,DELETE",
  "preflightContinue": false,
  "optionsSuccessStatus": 204
}

Any ideas what to do here? I get the feeling that the Android is baulking at the "*" origin, but have no idea what to put in place for a mobile app.

Or am I barking up the wrong tree pletely?

I'm using superagent to upload files from my React Native app. It works perfectly fine on iOS, but on Android it gives this error:

Possible causes: the network is offline, Origin is not allowed by Access-Control-Allow-Origin, the page is being unloaded, etc.

I've created a minimal example here https://snack.expo.io/@twohill/upload-example and copied the code below in case the snack goes away:

import React, { Component } from 'react';
import { Text, View, StyleSheet, TouchableOpacity } from 'react-native';
import Constants from 'expo-constants';
import * as DocumentPicker from 'expo-document-picker';
import superagent from 'superagent';

import AssetExample from './ponents/AssetExample';
import { Card } from 'react-native-paper';

const upload = (file, setMessage) => {
  const { name } = file;

  superagent.post('https://example/uploads') // <-- change this URL to your server that accepts uploads and is CORS enabled
    .set('Authorization', 'BEARER xxxyyy') // <-- JWT token here
    .attach('uri', file, name)
    .then(
      result => setMessage(JSON.stringify({result})),
      error => setMessage(JSON.stringify({error}))
      );
};

const pickDocuments = async (setMessage) => {
  const file = await DocumentPicker.getDocumentAsync({ copyToCacheDirectory: true });
  if (file.type === "success") {
    upload(file, setMessage);
  }
}

export default class App extends Component {
  state = {
    message: null,
  }
  render() {
    const { message } = this.state;
    return (

      <View style={styles.container}>
       <TouchableOpacity onPress={() => pickDocuments(message => this.setState({message}))}>
        <Text style={styles.paragraph}>
          Tap to upload a file
        </Text>
        <Card>
          <AssetExample />
        </Card>
        <Card><Text>{message}</Text></Card>
         </TouchableOpacity>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    paddingTop: Constants.statusBarHeight,
    backgroundColor: '#ecf0f1',
    padding: 8,
  },
  paragraph: {
    margin: 24,
    fontSize: 18,
    fontWeight: 'bold',
    textAlign: 'center',
  },
});

If I console.log the error it gives the following:

Request has been terminated
Possible causes: the network is offline, Origin is not allowed by Access-Control-Allow-Origin, the page is being unloaded, etc.
* http://192.168.1.3:19001/node_modules%5Cexpo%5CAppEntry.bundle?platform=android&dev=true&minify=false&hot=false:282311:24 in crossDomainError
- node_modules\@sentry\utils\dist\instrument.js:224:24 in <anonymous>
- node_modules\event-target-shim\dist\event-target-shim.js:818:39 in EventTarget.prototype.dispatchEvent
- node_modules\react-native\Libraries\Network\XMLHttpRequest.js:566:23 in setReadyState
- node_modules\react-native\Libraries\Network\XMLHttpRequest.js:388:25 in __didCompleteResponse
- node_modules\react-native\Libraries\vendor\emitter\EventEmitter.js:190:12 in emit
- node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:436:47 in __callFunction
- node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:111:26 in __guard$argument_0
- node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:384:10 in __guard
- node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:110:17 in __guard$argument_0
* [native code]:null in callFunctionReturnFlushedQueue

As far as I can tell, on Android the app never tries an upload. My server runs express and has the cors middleware enabled with the default configuration

{
  "origin": "*",
  "methods": "GET,HEAD,PUT,PATCH,POST,DELETE",
  "preflightContinue": false,
  "optionsSuccessStatus": 204
}

Any ideas what to do here? I get the feeling that the Android is baulking at the "*" origin, but have no idea what to put in place for a mobile app.

Or am I barking up the wrong tree pletely?

Share Improve this question edited Feb 7, 2020 at 8:06 sideshowbarker 88.3k29 gold badges215 silver badges212 bronze badges asked Feb 5, 2020 at 2:23 altalt 2,6162 gold badges25 silver badges29 bronze badges 2
  • From stackoverflow./a/46966838/441757 I see superagent is the source of that error which mentions “Origin is not allowed by Access-Control-Allow-Origin” — specifically the code at github./visionmedia/superagent/blob/master/src/…. But that “Origin is not allowed by Access-Control-Allow-Origin” part of the message is misleading, because Node doesn’t enforce the same-origin policy; therefore CORS and the Access-Control-Allow-Origin header aren’t relevant. So the actual cause of the error is something else, not CORS. – sideshowbarker Commented Feb 5, 2020 at 5:02
  • The answers at stackoverflow./a/39454628/441757 and stackoverflow./a/32570827/441757 may or may not be relevant here: “The cause is ANY button click getting treated as a submit and the form not having a submit action target. One solution is to add a preventDefault event handler.” – sideshowbarker Commented Feb 5, 2020 at 5:52
Add a ment  | 

3 Answers 3

Reset to default 2

Thanks for everyone's help. With the confirmation that the error isn't likely to be CORS I did some digging with the debugger and found the actual error: Binary FormData part needs a content-type header

From there, I was able to do even more digging, and found that I was being led astray (at least on Android) by the documentation https://visionmedia.github.io/superagent/#attaching-files as the options map I was sending was being ignored

With the help of the react-native-mime-types package the fixed code looks like this:

  //snip
  const fileWithMime = { ...file, type: mime.lookup(name) };

  request.post(`${SERVER_URL}/uploads`)
    .set('Authorization', cookie)
    .withCredentials()
    .attach('uri', fileWithMime)

For me the issue was with the ios/MyProject.xcodeproj/project.pbxproj ALLOWED_URL_PREFIXES (for Android it is android/app/src/main/AndroidManifest.xml AllowedUrlPrefixes)

        <meta-data
            android:name="AllowedUrlPrefixes"
            android:value='https://bitpay.,...' />

I was missing my domain on this list and XMLHttpRequest (used by superagent library) always returned 0 status code :(

http client in react-native is basically native http client with JS bridge, it has no CORS restriction like the web, so you don't need to allow CORS in your express server.

In terms of superagent, it seems you need some tweaking to use it in react-native, checkout this issue

本文标签: