はじめに

NativeScript(NatvieScript-vue) で、非 SSL サイトへのリクエストを許可する方法。

TL;DR

  • Android: AndroidManifest.xmlusesCleartextTraffic の設定追加
  • iOS: Info.plistNSAllowsArbitraryLoads の設定追加
  • よりセキュアな方法もあるので、各自の状況や環境に合わせて要設定

目次

  1. はじめに
  2. TL;DR
  3. 環境・条件
  4. 詳細
    1. 問題点
    2. 解決方法
    3. 簡単な解説
      1. Android
      2. iOS
  5. まとめ
  6. 参考文献

環境・条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
$ sw_vers
ProductName: Mac OS X
ProductVersion: 10.15.4
BuildVersion: 19E287

$ node -v
v12.7.0

$ npm -v
6.10.3

$ tns --version
6.4.0

$ grep -C1 version package.json
"tns-android": {
"version": "6.0.0"
},
"tns-ios": {
"version": "6.4.2"
}

$ tns plugin
Dependencies:
┌──────────────────────────────┬─────────┐
│ Plugin │ Version │
│ @nativescript/theme │ ^2.2.1 │
│ @vue/devtools │ ^5.0.6 │
│ axios │ ^0.19.2 │
│ nativescript-background-http │ ^4.2.1 │
│ nativescript-socketio │ ^3.2.1 │
│ nativescript-toasty │ ^1.3.0 │
│ nativescript-vue │ ^2.4.0 │
│ nativescript-vue-devtools │ ^1.2.0 │
│ tns-core-modules │ ^6.0.0 │
└──────────────────────────────┴─────────┘
Dev Dependencies:
┌────────────────────────────────────┬─────────┐
│ Plugin │ Version │
│ @babel/core │ ^7.0.0 │
│ @babel/preset-env │ ^7.0.0 │
│ babel-loader │ ^8.0.2 │
│ nativescript-dev-webpack │ ^1.0.0 │
│ nativescript-vue-template-compiler │ ^2.0.0 │
│ nativescript-worker-loader │ ~0.9.0 │
│ node-sass │ ^4.9.2 │
│ vue-loader │ ^15.4.0 │
└────────────────────────────────────┴─────────┘

詳細

問題点

axios を使って、非 SSL の API Endpoint(ex: http://path/to/api)へのリクエストを行ったところ、以下のように Request failed with status code null となった。

Chrome の Console

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
{
"error": {
"message": "Request failed with status code null",
"name": "Error",
"stack": "Error: Request failed with status code null\n at createError (file:///data/data/com.jet.truckshop/files/app/vendor.js:54992:15)\n at settle (file:///data/data/com.jet.truckshop/files/app/vendor.js:55197:12)\n at XMLHttpRequest.handleLoad [as onreadystatechange] (file:///data/data/com.jet.truckshop/files/app/vendor.js:54499:7)\n at XMLHttpRequest.emitEvent (file:///data/data/com.jet.truckshop/files/app/vendor.js:47927:30)\n at XMLHttpRequest._setRequestError (file:///data/data/com.jet.truckshop/files/app/vendor.js:47951:10)\n at file:///data/data/com.jet.truckshop/files/app/vendor.js:48048:13",
"config": {
"url": "http://path/to/api/login",
"method": "post",
"data": "{\"user_id\":\"dummy\"}",
"headers": {
"Accept": "application/json, text/plain, */*",
"Content-Type": "application/json;charset=utf-8"
},
"transformRequest": [
null
],
"transformResponse": [
null
],
"timeout": 0,
"xsrfCookieName": "XSRF-TOKEN",
"xsrfHeaderName": "X-XSRF-TOKEN",
"maxContentLength": -1
}
},
"request": {
"UNSENT": 0,
"OPENED": 1,
"HEADERS_RECEIVED": 2,
"LOADING": 3,
"DONE": 4,
"_responseType": "",
"_listeners": {},
"_readyState": 4,
"_options": {
"url": "http://path/to/api/login",
"method": "POST",
"headers": {
"Accept": "application/json, text/plain, */*",
"Content-Type": "application/json;charset=utf-8"
},
"content": "{\"user_id\":\"dummy\"}"
},
"timeout": 0,
"_errorFlag": true,
"_response": {},
"_responseTextReader": null,
"_headers": null,
"_status": null,
"_sendFlag": false
},
"response": {
"data": "",
"status": null,
"statusText": "",
"headers": {},
"config": "[Circular]",
"request": "[Circular]"
},
"config": "[Circular]",
"message": "Request failed with status code null"
}

Chrome の Network タブ

なお、Android エミュレータ上では問題なく動作したため、実機を使うフェーズになって初めて気付いた。

  • Android エミュレータ: OK
  • Android 実機: NG
  • iOS エミュレータ: NG
  • iOS 実機: NG

解決方法

javascript - How to call axios/api call in Nativescript application - Stack Overflow に書かれている通りに対応すれば良い。

Android は app/App_Resources/Android/src/main/AndroidManifest.xml を編集。

1
2
3
4
5
6
7
8
9
   ...
<application
android:name="com.tns.NativeScriptApplication"
android:allowBackup="true"
android:icon="@drawable/icon"
android:label="@string/app_name"
+ android:usesCleartextTraffic="true"
android:theme="@style/AppTheme">
...

iOS は app/App_Resources/iOS/Info.plist を編集。

1
2
3
4
5
6
7
8
9
10
11
    ...
<key>LSRequiresIPhoneOS</key>
<true/>
+ <key>NSAppTransportSecurity</key>
+ <dict>
+ <key>NSAllowsArbitraryLoads</key>
+ <true/>
+ </dict>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
...

これらを編集後にビルドしたファイルで http へのリクエストが成功するのを確認できた。

簡単な解説

これらは NativeScript の問題ではなく、単に Android/iOS アプリ開発時の設定に起因する問題(というか事象)になる。
(ネイティブアプリ開発経験がある人からすれば当然の話になると思うが)

Android

以下が参考になる。

上記参考ページにも書かれている通り、今回の usesCleartextTraffic で対応する方法以外にも network_security_config.xml で詳細に設定する方法があるらしい。

ただし、 network_security_config.xml は NativeScript がデフォルトで用意してくれないので、自前で作成する必要がある(はず)。

iOS

以下が参考になる。

今回は手っ取り早く NSAllowsArbitraryLoads で全許容しているが、実際には NSExceptionDomainsNSTemporaryExceptionAllowsInsecureHTTPLoads とで設定する方法を用いた方がよりセキュア。

まとめ

  • Android: AndroidManifest.xmlusesCleartextTraffic の設定追加
  • iOS: Info.plistNSAllowsArbitraryLoads の設定追加
  • よりセキュアな方法もあるので、各自の状況や環境に合わせて要設定

参考文献

関連記事