Safari Web Inspector 流量攔截延遲現象

前言 最近遇到一個需求,我們的 mobile app 需要用 web view 開啟某個網頁。因為該網頁的認證方式最近有調整,app 端也得跟著修改。也因此這次 iOS 也要從原本的 SFSafariViewController 換成 MKWebView 來實作內嵌網頁,結果問題就這樣冒出來了。 過程 事情大概是這樣: 當我在頁面上點擊某個按鈕,會跳轉到另一個網頁,然後這個新頁面會自動彈出一個視窗到最前面。奇怪的是,首頁本身載入沒問題,如果在首頁觸發彈窗也都正常。但只要是跳轉後的頁面,彈窗就會出狀況。 一開始我懷疑是 cookie 沒有正確傳遞,或是 header 有缺漏,所以重點都放在這些地方。用 Safari Web Inspector 看 network,也沒看到什麼明顯的錯誤,只是覺得很怪,怎麼 request、response 的紀錄都不太完整,像是 header 竟然是空的。當下還以為這就是正常現象,只好很麻煩地回到 app 裡面用 NSLog 確認內容。 後來不知道哪根筋突然不對,想說試試看手動按一下重新整理。沒想到這一按,network 頁面所有的請求紀錄都跑出來了,header、response 什麼資訊都有。這時才發現,原來是某個 JS 檔在執行時出錯。仔細追查才知道,是網頁開發者寫的「取得使用者瀏覽器類型」的 function 沒有考慮到 MKWebView 發出的 User-Agent,導致相關 UI 無法正常顯示。 那段程式碼還註解「若非不得已,盡量少用此方法」,是一個拿 14 年前 stackoverflow 上的解答的函式XD User-Agent:WKWebView 預設的 User-Agent 與 Safari 或 SFSafariViewController 不同,若網頁端有依賴 User-Agent 進行判斷,需特別注意。 關於 Safari Web Inspector 的觀察 這次讓我發現,Safari Web Inspector 其實有個不太直覺的地方。你必須先在手機上打開那個網頁,Inspector 的 develop 頁籤才會出現該網頁的選項。也就是說,當你點進去看的時候,網頁內容已經載入完成了,Inspector 只會顯示最後的狀態。過程中的流量、請求紀錄根本沒被捕捉到,除非你手動重新整理一次,才會看到完整的 network 資訊。 ...

July 11, 2025 · 1 分鐘

Azure ReplaceTokens 字串轉義問題

問題現象 在某個專案中,CI/CD 過程需要將設定以 token 替換的方式寫入 JSON 檔案,其中一個欄位包含多行文字內容。在 pipeline 中使用了 qetza.replacetokens.replacetokens-task.replacetokens@5 這個 Azure DevOps Task: - task: qetza.replacetokens.replacetokens-task.replacetokens@5 displayName: 'Replace token in xxx-config.json' inputs: targetFiles: '$(System.ArtifactsDirectory)\\drop\\$(Build.BuildId)\\xxx-config.json' 完成替換並部署到測試環境後,發現無法正常啟動。檢查部署產出的 JSON 檔案時,發現原本應為多行的字串中的 \n,在處理後變成了 \\n。 這導致程式在解析該欄位時,無法還原出原本的格式,進而發生錯誤。 問題原因 查閱文件後發現,ReplaceTokens 預設會對變數值進行 escape 處理。這對一般情況有幫助,但對於需要保留格式的多行字串(如憑證或編碼內容)就會導致格式錯誤。 解法:使用 noescape() 函數停用自動 escape ReplaceTokens 支援開啟轉換函數(Transformations)功能,可用來自訂變數處理方式。具體做法如下: 步驟 1:啟用 enableTransforms 在 ReplaceTokens 的 YAML 設定中加上 enableTransforms: true - task: qetza.replacetokens.replacetokens-task.replacetokens@5 displayName: 'Replace token in xxx-config.json' inputs: targetFiles: '$(System.ArtifactsDirectory)\\drop\\$(Build.BuildId)\\xxx-config.json' enableTransforms: true # 要加這行 步驟 2:修改 JSON,使用 noescape() 將目標檔案中的 token 改寫為: { "config_content": "#{noescape(multiline_config)}#" } 如此可讓 \n 在替換後仍保持原始格式,不會被轉成 \\n。 ...

July 9, 2025 · 1 分鐘

NuGet 套件遺失問題紀錄

Generated by Perplexity 問題說明 前陣子我遇到一個值得記錄的問題(至少對我而言):在維護一個有點舊的 .NET framework 專案時,發現透過 ProjectReference 方式依賴的 NuGet 套件無法自動還原。經過一番調查後,才發現這是 NuGet 在處理某些舊式專案時的一個行為特性。對 .NET 很不熟的我覺得可以記錄一下,以防未來又忘記。 專案結構與重現情境 MainApp.csproj ← 主專案,透過 ProjectReference 參考 SharedLibrary.csproj └── SharedLibrary.csproj └── <PackageReference Include="ICSharpCode.SharpZipLib" /> 我的 solution 中有兩個專案: MainApp.csproj:主應用程式專案 SharedLibrary.csproj:共用函式庫,透過 ProjectReference 被 MainApp 參考 在 SharedLibrary.csproj 中,使用了常見的壓縮解壓縮函式庫 ICSharpCode.SharpZipLib,並以以下方式引用: <PackageReference Include="ICSharpCode.SharpZipLib" Version="1.4.2" /> 然而在建置 MainApp.csproj 時,卻發現 SharpZipLib 的相關類別無法解析,沒有編譯出 .dll 檔。反觀其他套件如 NPOI 和 EntityFramework 則一切正常,讓我相當困惑(因為不論是在 prodution 或是 test 環境中都沒看到 .dll 缺失的問題)。 至於為什麼會發生這種事情,我想…就技術債這三個字吧 嘗試與排查過程 一開始我找問題的方式比較粗暴簡單,那就是找其他也有引用 SharedLibrary.csproj,但卻可以成功編譯的專案,看他們跟有問題的專案差在哪裡。於是我找到了其他專案多的這一行: <PackageReference Include="EntityFramework" Version="5.0.0" /> 神奇的事情發生了,SharpZipLib 居然也能正確還原了! 這讓我開始懷疑問題出在 NuGet 的套件還原邏輯上。經過研究,終於找到癥結所在。 ...

July 7, 2025 · 1 分鐘

AI 時代的軟體工程師:Andrej Karpathy 影片觀後感

今天看到一段影片,是 Andrej Karpathy(Vibe coding 一詞之創始人)的演講片段 中英雙字版本 軟體開發的三個重要階段 他提到目前軟體開發演進可分為三個重要階段: (擷取自影片) Software 1.0:由工程師手寫的程式碼。 Software 2.0:神經網路的權重,工程師不再直接撰寫程式,而是透過調整資料集並運行優化器來訓練模型,進而產生這些程式碼(權重)。例如特斯拉自動駕駛系統中許多功能已從 1.0 轉為 2.0。 Software 3.0:大型語言模型 (LLMs) 可以透過自然語言提示來「程式設計」。英文本身成為一種強大的程式語言,讓每個人都成為潛在的程式設計師。 LLMs 的角色與挑戰 Karpathy 將大型語言模型(LLMs)比喻為新型的作業系統,類似 1960 年代初期集中於雲端的電腦運算。他認為 LLMs 具備類似人類的「心理」,擁有百科全書般的知識與超能力般的記憶力,但同時也存在認知缺陷,例如會產生幻覺、智慧表現不穩定,以及類似「順向失憶症」的特性——每次互動後會清除記憶。因此,Karpathy 提醒我們在利用其強大能力的同時,必須特別注意並避開這些弱點。 Prompt 成為新時代的程式語言 隨著 LLMs 的出現,「prompts」本身成為了一種新型態的程式,這大幅降低了寫程式的門檻。現在,人人都能利用 AI 輕鬆產生基本的程式碼。Karpathy 甚至分享,他自己在完全不懂 Swift 語言的情況下,僅用一天就透過 Vibe Coding 完成了一個基本的 iOS 應用程式。 軟體工程師價值的轉變 然而,因為 LLMs 仍然是「會犯錯的系統」,Karpathy 認為軟體工程師的價值也隨之轉變——從純粹的程式碼撰寫,轉向設計與維護人機協作的「生成—驗證」循環。AI 負責生成內容,而人類工程師則負責驗證其輸出。他以鋼鐵人的鋼鐵裝為例,強調我們應將 AI 視為「人類的增強工具」,而非完全自主的 agent。我們必須「將 AI 拴住(keep the AI on the leash)」,確保其輸出符合預期且無潛在問題,並避免 AI 生成過於龐大的修改,導致審核困難。現在軟體工程師的的任務是「讓這個循環盡可能地快速進行」。軟體工程師的價值轉向設計更有效率的工具和介面(GUI),使人類能夠快速地審核與驗證 AI 所生成的結果。 個人觀後感 以上僅提到一些我認為的重要觀點,影片還有許多內容,有興趣的人可以自己去看,蠻建議花時間看完的。 ...

July 6, 2025 · 1 分鐘

n8n 本地部署紀錄

A basic record of local n8n deployment. Steps Docker docker-compose.yml version: "3.9" volumes: n8n_storage: services: n8n: image: n8nio/n8n:latest restart: always ports: - "127.0.0.1:5678:5678" volumes: - n8n_storage:/home/node/.n8n environment: - WEBHOOK_URL=https://<your-ngrok-domain>.ngrok-free.app # External webhook URL Usage Expose local port 5678 to the internet via ngrok: ngrok http http://localhost:5678 Copy the generated ngrok public URL (e.g., https://xxxx-xxx-xxx.ngrok-free.app). Edit the docker-compose.yml file and replace <your-ngrok-domain> in WEBHOOK_URL with your actual ngrok URL. Start the n8n service using Docker Compose: ...

July 4, 2025 · 1 分鐘