Skip to content

Commit b659d7f

Browse files
realaboofacebook-github-bot-3
authored andcommitted
Get response url from XMLHttpRequest
Summary: An HTTP request may be redirected to another URL, sometimes we need to know the URL where the response comes from. If the server is in control, we can add an HTTP header X-Request-URL for the redirect URL. However there will be cases that 3rd party services are used. This PR retrieves the response URL from native networking module and passes to it XMLHttpRequest. The fetch API built on XMLHttpRequest also benefits from this feature. Closes facebook/react-native#4981 Reviewed By: svcscm Differential Revision: D2811392 Pulled By: lexs fb-gh-sync-id: 3ec356fb92f8011b6a243d6879172877a3dc498a
1 parent 473f9bc commit b659d7f

6 files changed

Lines changed: 132 additions & 67 deletions

File tree

Examples/UIExplorer/XHRExample.android.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ var {
2828

2929
var XHRExampleHeaders = require('./XHRExampleHeaders');
3030
var XHRExampleCookies = require('./XHRExampleCookies');
31+
var XHRExampleFetch = require('./XHRExampleFetch');
3132

3233

3334
// TODO t7093728 This is a simplified XHRExample.ios.js.
@@ -281,6 +282,11 @@ exports.examples = [{
281282
render() {
282283
return <FormUploader/>;
283284
}
285+
}, {
286+
title: 'Fetch Test',
287+
render() {
288+
return <XHRExampleFetch/>;
289+
}
284290
}, {
285291
title: 'Headers',
286292
render() {

Examples/UIExplorer/XHRExample.ios.js

Lines changed: 3 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ var {
3131
} = React;
3232

3333
var XHRExampleHeaders = require('./XHRExampleHeaders');
34+
var XHRExampleFetch = require('./XHRExampleFetch');
3435

3536
class Downloader extends React.Component {
3637

@@ -304,54 +305,6 @@ class FormUploader extends React.Component {
304305
}
305306
}
306307

307-
class FetchTest extends React.Component {
308-
309-
constructor(props) {
310-
super(props);
311-
this.state = {
312-
responseText: null,
313-
};
314-
}
315-
316-
submit(uri: String) {
317-
fetch(uri).then((response) => {
318-
return response.text();
319-
}).then((body) => {
320-
this.setState({responseText: body});
321-
});
322-
}
323-
324-
render() {
325-
326-
var response = this.state.responseText ? (
327-
<View style={{marginTop: 10}}>
328-
<Text style={styles.label}>Server response:</Text>
329-
<TextInput
330-
editable={false}
331-
multiline={true}
332-
defaultValue={this.state.responseText}
333-
style={styles.textOutput}
334-
/>
335-
</View>
336-
) : null;
337-
338-
return (
339-
<View>
340-
<Text style={styles.label}>Edit URL to submit:</Text>
341-
<TextInput
342-
returnKeyType="go"
343-
defaultValue="http://www.posttestserver.com/post.php"
344-
onSubmitEditing={(event)=> {
345-
this.submit(event.nativeEvent.text);
346-
}}
347-
style={styles.textInput}
348-
/>
349-
{response}
350-
</View>
351-
);
352-
}
353-
}
354-
355308
exports.framework = 'React';
356309
exports.title = 'XMLHttpRequest';
357310
exports.description = 'XMLHttpRequest';
@@ -366,9 +319,9 @@ exports.examples = [{
366319
return <FormUploader/>;
367320
}
368321
}, {
369-
title: 'fetch test',
322+
title: 'Fetch Test',
370323
render() {
371-
return <FetchTest/>;
324+
return <XHRExampleFetch/>;
372325
}
373326
}, {
374327
title: 'Headers',
@@ -432,19 +385,4 @@ var styles = StyleSheet.create({
432385
fontSize: 16,
433386
fontWeight: '500',
434387
},
435-
label: {
436-
flex: 1,
437-
color: '#aaa',
438-
fontWeight: '500',
439-
height: 20,
440-
},
441-
textOutput: {
442-
flex: 1,
443-
fontSize: 17,
444-
borderRadius: 3,
445-
borderColor: 'grey',
446-
borderWidth: 1,
447-
height: 200,
448-
paddingLeft: 8,
449-
},
450388
});
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/**
2+
* The examples provided by Facebook are for non-commercial testing and
3+
* evaluation purposes only.
4+
*
5+
* Facebook reserves all rights not expressly granted.
6+
*
7+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
8+
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
9+
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
10+
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
11+
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
12+
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
13+
*
14+
* @flow
15+
*/
16+
'use strict';
17+
18+
var React = require('react-native');
19+
var {
20+
StyleSheet,
21+
Text,
22+
TextInput,
23+
View,
24+
Platform,
25+
} = React;
26+
27+
28+
class XHRExampleFetch extends React.Component {
29+
30+
constructor(props: any) {
31+
super(props);
32+
this.state = {
33+
responseText: null,
34+
};
35+
this.responseURL = null;
36+
}
37+
38+
submit(uri: String) {
39+
fetch(uri).then((response) => {
40+
this.responseURL = response.url;
41+
return response.text();
42+
}).then((body) => {
43+
this.setState({responseText: body});
44+
});
45+
}
46+
47+
render() {
48+
49+
var responseURL = this.responseURL ? (
50+
<View style={{marginTop: 10}}>
51+
<Text style={styles.label}>Server response URL:</Text>
52+
<Text>{this.responseURL}</Text>
53+
</View>
54+
) : null;
55+
56+
var response = this.state.responseText ? (
57+
<View style={{marginTop: 10}}>
58+
<Text style={styles.label}>Server response:</Text>
59+
<TextInput
60+
editable={false}
61+
multiline={true}
62+
defaultValue={this.state.responseText}
63+
style={styles.textOutput}
64+
/>
65+
</View>
66+
) : null;
67+
68+
return (
69+
<View>
70+
<Text style={styles.label}>Edit URL to submit:</Text>
71+
<TextInput
72+
returnKeyType="go"
73+
defaultValue="http://www.posttestserver.com/post.php"
74+
onSubmitEditing={(event)=> {
75+
this.submit(event.nativeEvent.text);
76+
}}
77+
style={styles.textInput}
78+
/>
79+
{responseURL}
80+
{response}
81+
</View>
82+
);
83+
}
84+
}
85+
86+
var styles = StyleSheet.create({
87+
textInput: {
88+
flex: 1,
89+
borderRadius: 3,
90+
borderColor: 'grey',
91+
borderWidth: 1,
92+
height: Platform.OS === 'android' ? 44 : 30,
93+
paddingLeft: 8,
94+
},
95+
label: {
96+
flex: 1,
97+
color: '#aaa',
98+
fontWeight: '500',
99+
height: 20,
100+
},
101+
textOutput: {
102+
flex: 1,
103+
fontSize: 17,
104+
borderRadius: 3,
105+
borderColor: 'grey',
106+
borderWidth: 1,
107+
height: 200,
108+
paddingLeft: 8,
109+
},
110+
});
111+
112+
module.exports = XHRExampleFetch;

Libraries/Network/RCTNetworking.m

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,8 @@ - (void)sendRequest:(NSURLRequest *)request
375375
headers = response.MIMEType ? @{@"Content-Type": response.MIMEType} : @{};
376376
status = 200;
377377
}
378-
NSArray<id> *responseJSON = @[task.requestID, @(status), headers];
378+
id responseURL = response.URL ? response.URL.absoluteString : [NSNull null];
379+
NSArray<id> *responseJSON = @[task.requestID, @(status), headers, responseURL];
379380
[_bridge.eventDispatcher sendDeviceEventWithName:@"didReceiveNetworkResponse"
380381
body:responseJSON];
381382
});

Libraries/Network/XMLHttpRequestBase.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class XMLHttpRequestBase {
3232
responseHeaders: ?Object;
3333
responseText: ?string;
3434
status: number;
35+
responseURL: ?string;
3536

3637
upload: ?{
3738
onprogress?: (event: Object) => void;
@@ -69,6 +70,7 @@ class XMLHttpRequestBase {
6970
this.responseHeaders = undefined;
7071
this.responseText = '';
7172
this.status = 0;
73+
delete this.responseURL;
7274

7375
this._requestId = null;
7476

@@ -110,11 +112,16 @@ class XMLHttpRequestBase {
110112
}
111113
}
112114

113-
_didReceiveResponse(requestId: number, status: number, responseHeaders: ?Object): void {
115+
_didReceiveResponse(requestId: number, status: number, responseHeaders: ?Object, responseURL: ?string): void {
114116
if (requestId === this._requestId) {
115117
this.status = status;
116118
this.setResponseHeaders(responseHeaders);
117119
this.setReadyState(this.HEADERS_RECEIVED);
120+
if (responseURL || responseURL === '') {
121+
this.responseURL = responseURL;
122+
} else {
123+
delete this.responseURL;
124+
}
118125
}
119126
}
120127

ReactAndroid/src/main/java/com/facebook/react/modules/network/NetworkingModule.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,7 @@ private void onResponseReceived(int requestId, Response response) {
289289
args.pushInt(requestId);
290290
args.pushInt(response.code());
291291
args.pushMap(headers);
292+
args.pushString(response.request().urlString());
292293

293294
getEventEmitter().emit("didReceiveNetworkResponse", args);
294295
}

0 commit comments

Comments
 (0)