級別:★☆☆☆☆
標籤:「Flutter web」「Dart Server」「blocked by CORS Policy」「跨域」
作者: WYW
審校: QiShare團隊
前言 筆者最近了解了Flutter web相關的內容,本文會分享建立Flutter web專案、Flutter web專案預覽,Flutter web專案和Flutter mobile(Flutter Android/iOS)專案的差別、搭建簡易Dart伺服器(解決跨域問題)、上線Flutter web專案相關內容。
一、建立Flutter web 專案
準備Flutter web 環境
更新本地環境為 beta channel最新版。(dev channel 也可以)
flutter channel beta
flutter upgrade
Flutter有如下4個channel:
flutter channel
Flutter channels:
beta
* dev
master
stable
複製程式碼
Flutter 官方建議使用 stable 的channel。
master 是當前最新的channel;
dev 是當前最新的充分測試後的channel;
beta是每個月Flutter官方調整選出來的最好的dev的channel,並提升為beta channel;
stable是Flutter 認為是當前最穩定的channel。
穩定性而言:master < dev < beta < stable 。更多內容可檢視:Flutter build release channels
開啟專案支援Flutter web
flutter config --enable-web
如果想在當前已有專案Flutter mobile專案的基礎上,新增Flutter web支援,可 cd 到 Flutter mobile 專案目錄下,新增Flutter web支援。
新建Flutter web專案
如果之前沒有建立過Flutter 專案,新建一個Flutter web專案可以使用如下命令。
flutter create 專案名(小寫)
如:flutter create qi_flutter_web_demo
現有專案生成Flutter web 相關檔案
如果之前建立過Flutter 專案,想現有專案生成web資料夾及index.html等檔案可使用如下命令。
flutter create .
執行專案命令:flutter run -d chrome
遇到問題:執行失敗
執行失敗報錯如下:
wangyongwangdeiMac:qi_flutter_page wangyongwang$ flutter run -d chrome
Flutter assets will be downloaded from storage.flutter-io.cn. Make sure you trust
this source!
Downloading Web SDK... 1.1s
Launching lib/main.dart on Chrome in debug mode...
Error compiling dartdevc module:qi_flutter_page|lib/main_web_entrypoint.ddc.js
packages/qi_flutter_page/main_web_entrypoint.dart:9:18: Error: Too few positional arguments:
1 required, 0 given.
entrypoint.main();
^ 複製程式碼
AssetNotFoundException: qi_flutter_page|lib/main_web_entrypoint.ddc.js
Failed after 23.3s
Building application for the web... 33.5s
Failed to build application for the Web.
猜測原因:訪問網址https://storage.flutter-io.cn.不可達
起初,筆者猜測原因是這個網址https://storage.flutter-io.cn.訪問不可達;不過試過執行新建立的Flutter web專案,發現新建的Flutter web專案可以正常執行,可以排除問題不在於網址https://storage.flutter-io.cn.訪問不可達。
繼續看這段報錯,可以發現Flutter web 專案的main 方法中不能有引數。
packages/qi_flutter_page/main_web_entrypoint.dart:9:18: Error: Too few positional arguments: 1 required, 0 given. entrypoint.main(); ^
AssetNotFoundException: qi_flutter_page|lib/main_web_entrypoint.ddc.js
Failed after 22.9s
問題在於main方法中引數
執行Flutter web 專案的時候,main方法中不能有引數。
void main(List<String> args) {
}
// 刪除main方法名中的引數後,可以正常執行。
void main() {
}
複製程式碼
筆者以之前寫的專案qi_flutter_page為例:執行起來的效果如下:
上週和同事CH聊天學到的內容:Flutter web專案顯示的網頁的特點:
顯示網頁原始碼的時候,可以網頁發現顯示的內容是html的body 中巢狀的main.dart.js。
二、Flutter web 專案預覽
執行Flutter web專案 預設會在Chrome瀏覽器中顯示,不過在本機的Safari 瀏覽器中及模擬器中的瀏覽器中輸入相應的網址,也可以顯示相應的檢視。
三、Flutter web專案 與 Flutter mobile 專案的不同
筆者在把現有Flutter mobile專案,直接支援Flutter web 的過程中遇到了網路請求報異常的問題,另外簡單測試了2個三方庫的在Flutter web專案中的體現。
HttpClient() 不能用於Flutter web 專案
try {
HttpClient client = HttpClient();
} catch (e) {
print('捕獲異常:$e');
}
複製程式碼
捕獲異常:NoSuchMethodError: invalid member on null: 'indexOf'
Flutter web專案的網路請求可以使用html.httpRequest。
import 'dart:html' as html;
html.HttpRequest.request(url).then((responseValue) {
});
複製程式碼
三方庫支援情況
筆者這裡舉2個自己使用過三方庫,shared_preferences、url_launcher。
下列程式碼對於Flutter web 專案中仍然支援開啟載入url的視窗。
String soUrl = 'https://www.so.com';
if (await canLaunch(soUrl)) {
await launch(soUrl);
}
複製程式碼
由如下程式碼及相應結果可知,shared_preferences 也支援 Flutter Web 專案。
void _incrementCounter() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
int counter = (prefs.getInt('counter') ?? 0) + 1;
print('Pressed $counter times.');
await prefs.setInt('counter', counter);
}
複製程式碼
ListTile2 Pressed 1 times. ListTile2 Pressed 2 times. ListTile2 Pressed 3 times. ListTile2 Pressed 4 times. ListTile2 Pressed 5 times.
三方庫一般會註明支援的平臺(Android、iOS或Web)。 url_launcher 5.4.1支援 Flutter web 專案。 shared_preferences 支援 Flutter web 專案。 sqflite自己註明了支援Android和iOS,是否支援Flutter web沒有做說明。
如下圖所示:
四、簡易Dart伺服器
使用如下程式碼,可以本地啟動一個Dart服務。
main() async {
var server = await HttpServer.bind(InternetAddress.loopbackIPv4, 9988);
await for (var request in server) {
request.response
..headers.contentType = ContentType('text', 'plain', charset: 'utf-8')
..write('Hello Dart! 你好Dart')
..close();
}
}
複製程式碼
瀏覽器中直接請求http://127.0.0.1:9988 示意圖如下:
筆者在Flutter web專案中請求,http://127.0.0.1:9988的時候,遇到了跨域問題,下邊分享下相關問題及處理方式。
跨域問題
跨域問題描述
跨域資源共享(CORS) 是一種機制,它使用額外的 HTTP 頭來告訴瀏覽器 讓執行在一個 origin (domain) 上的Web應用被准許訪問來自不同源伺服器上的指定的資源。當一個資源從與該資源本身所在的伺服器不同的域、協議或埠請求一個資源時,資源會發起一個跨域 HTTP 請求。
比如,站點 domain-a.com 的某 HTML 頁面通過 的 src 請求 domain-b.com/image.jpg。網…
出於安全原因,瀏覽器限制從指令碼內發起的跨源HTTP請求。 例如,XMLHttpRequest和Fetch API遵循同源策略。 這意味著使用這些API的Web應用程式只能從載入應用程式的同一個域請求HTTP資源,除非響應報文包含了正確CORS響應頭。 引自HTTP訪問控制(CORS)
舉2個例子比如我們自身當前域名為abc.com, 訪問def.com 會出現跨域的問題。 比如我們自身當前域名為abc.com埠號為1234(abc.com:1234),那麼訪問abc.com:5678也會出現跨域問題。
筆者使用Flutter web專案請求服務端資源的時候遇到了跨域問題。
http://localhost:55355/#/ 中的內容訪問http://127.0.0.1:9988
Flutter web專案跨域現象圖現象圖如下:
出現當前跨域問題的原因是埠號不同,訪問Flutter web專案的url 和 請求服務端資源的url的 埠號
不同。
請求的響應頭中設定可跨域的origin,解決跨域問題
設定跨域的url 有2種設定方式:
- 1.設定一個或多個url;
- 如:request.response ..headers .add('Access-Control-Allow-Origin', request.headers['origin'])
- 2.設定跨域的值為*;
-
..headers.add('Access-Control-Allow-Origin', '*') 複製程式碼
-
注意:如果在本地測試使用,可以使用第二種方式,直接了當。但是一般線上的話最好使用第一種方式設定是否可以跨域請求。因為設定是否可以跨域,算是伺服器在響應瀏覽器請求資料時的一種保護策略。
其中重點是在響應頭中新增可以跨域的請求域。
..headers.add('Access-Control-Allow-Origin', 'http://localhost:55355')
如'Access-Control-Allow-Origin'可以指定特定的url,使url能夠跨域請求。
如有需要指定允許多個url進行跨域請求。可以根據請求的origin的值,判斷是否要做跨域響應頭的處理。
如:如下程式碼設定了當請求的origin 為http://localhost:63062
或 http://localhost:55355
的時候,會新增跨域處理的響應頭。
main() async {
var server = await HttpServer.bind(InternetAddress.loopbackIPv4, 9988);
var server = await HttpServer.bind(InternetAddress.loopbackIPv4, 9988);
await for (var request in server) {
var accessControlAllowOrigin = [
'http://localhost:63062',
'http://localhost:55355'
];
if (request.headers['origin'] != null) {
for (String tempAllowOrigin in accessControlAllowOrigin) {
if (request.headers['origin'].first.contains(tempAllowOrigin)) {
request.response
..headers
.add('Access-Control-Allow-Origin', request.headers['origin'])
// ..headers.add('Access-Control-Allow-Origin', '*')
..headers.contentType =
ContentType('text', 'plain', charset: 'utf-8')
..write('Hello Dart! 你好Dart 跨域')
..close();
}
}
} else {
request.response
..headers.contentType = ContentType('text', 'plain', charset: 'utf-8')
..write('Hello Dart! 你好Dart 不需要跨域')
..close();
}
}
}
複製程式碼
筆者在上邊說明了處理執行Flutter web專案的時候,處理本地服務端介面和Flutter web專案執行網址出現跨域問題的處理方式。(其實對於編譯後的Flutter web專案的產物直接放到自己的服務端專案的的靜態檔案目錄下的時候,不會出現上述問題) 有時候我們的請求內容可能就需要跨域去請求資料,而且對方如果也不便新增相應的跨域響應頭。此時,可使用Nginx 做反向代理來處理跨域問題。
Nginx 反向代理解決遠端跨域問題
server {
listen 9080;
server_name localhost;
location ~ /columns/Qtest {
proxy_pass https://testerhome.com;
}
}
複製程式碼
經上述處理,可以在本地的127.0.0.1:9080/columns/Qtest請求到 Qtest測試之道的 testerhome.com/columns/Qte… 相應資料。
Nginx 配置反向代理及rewrite訪問路徑可實現訪問遠端檔案不跨域。
location / {
proxy_pass https://weekly.75team.com;
}
location ~ /api/qiwuzhoukanWeb {
rewrite /api/qiwuzhoukanWeb /;
proxy_pass https://weekly.75team.com;
}
複製程式碼
經上述處理,可以在本地的127.0.0.1:9080/api/qiwuzhoukanWeb請求到 奇舞週刊的 weekly.75team.com 相應資料。
五、Flutter web 專案上線
flutter build web
會在專案的build 目錄中生成相應的資原始檔及html 和js檔案,把相關檔案放置到服務端靜態檔案目錄下即可。實現上線Flutter Web專案。
參考學習網址
- Flutter build release channels
- 使用 Flutter 構建 Web 應用
- HTTP訪問控制(CORS)
- 奇舞週刊
- Qtest 測試之道
- 詳解 CORS 跨域資源共享
- 跨域資源共享 CORS 詳解
- proxy_pass url 反向代理的坑
瞭解更多iOS及相關新技術,請關注我們的公眾號:
小編微信:可加並拉入《QiShare技術交流群》。
關注我們的途徑有:
QiShare(簡書)
QiShare(掘金)
QiShare(知乎)
QiShare(GitHub)
QiShare(CocoaChina)
QiShare(StackOverflow)
QiShare(微信公眾號)
推薦文章:
用AdHoc來測試iOS線上推送 Swift 5.1 (9) - 結構體和類
Swift 實現一個相容iOS、tvOS、OSX的抽象層
iOS Password AutoFill
iOS 給UILabel新增點選事件
用SwiftUI給檢視新增動畫
用SwiftUI寫一個簡單頁面
Swift 5.1 (8) - 列舉型別 iOS App啟動優化(三)—— 自己做一個工具監控App的啟動耗時
iOS App啟動優化(二)—— 使用“Time Profiler”工具監控App的啟動耗時
iOS App啟動優化(一)—— 瞭解App的啟動流程
奇舞週刊